Merge remote-tracking branch 'origin/two_edges' into new_Group
This commit is contained in:
commit
8742c87bfe
291 changed files with 9361 additions and 6719 deletions
|
|
@ -121,7 +121,7 @@ def run(
|
|||
),
|
||||
):
|
||||
"""
|
||||
Run the Langflow.
|
||||
Run Langflow.
|
||||
"""
|
||||
|
||||
configure(log_level=log_level, log_file=log_file)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from loguru import logger
|
|||
from sqlmodel import Session, col, select
|
||||
|
||||
from langflow.api.utils import remove_api_keys, validate_is_component
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListIds, FlowListRead
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListRead
|
||||
from langflow.initial_setup.setup import STARTER_FOLDER_NAME
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.services.database.models.flow import Flow, FlowCreate, FlowRead, FlowUpdate
|
||||
|
|
@ -258,9 +258,9 @@ async def download_file(
|
|||
return FlowListRead(flows=flows)
|
||||
|
||||
|
||||
@router.post("/multiple_delete/")
|
||||
@router.delete("/")
|
||||
async def delete_multiple_flows(
|
||||
flow_ids: FlowListIds, user: User = Depends(get_current_active_user), db: Session = Depends(get_session)
|
||||
flow_ids: List[UUID], user: User = Depends(get_current_active_user), db: Session = Depends(get_session)
|
||||
):
|
||||
"""
|
||||
Delete multiple flows by their IDs.
|
||||
|
|
@ -274,9 +274,7 @@ async def delete_multiple_flows(
|
|||
|
||||
"""
|
||||
try:
|
||||
deleted_flows = db.exec(
|
||||
select(Flow).where(col(Flow.id).in_(flow_ids.flow_ids)).where(Flow.user_id == user.id)
|
||||
).all()
|
||||
deleted_flows = db.exec(select(Flow).where(col(Flow.id).in_(flow_ids)).where(Flow.user_id == user.id)).all()
|
||||
for flow in deleted_flows:
|
||||
db.delete(flow)
|
||||
db.commit()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
from langflow.services.deps import get_monitor_service
|
||||
from langflow.services.monitor.schema import (
|
||||
MessageModelRequest,
|
||||
MessageModelResponse,
|
||||
TransactionModelResponse,
|
||||
VertexBuildMapModel,
|
||||
|
|
@ -66,6 +66,44 @@ async def get_messages(
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/messages", status_code=204)
|
||||
async def delete_messages(
|
||||
message_ids: List[int],
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
monitor_service.delete_messages(message_ids=message_ids)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/messages/{message_id}", response_model=MessageModelResponse)
|
||||
async def update_message(
|
||||
message_id: str,
|
||||
message: MessageModelRequest,
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
message_dict = message.model_dump(exclude_none=True)
|
||||
message_dict.pop("index", None)
|
||||
monitor_service.update_message(message_id=message_id, **message_dict)
|
||||
return MessageModelResponse(index=message_id, **message_dict)
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/messages/session/{session_id}", status_code=204)
|
||||
async def delete_messages_session(
|
||||
session_id: str,
|
||||
monitor_service: MonitorService = Depends(get_monitor_service),
|
||||
):
|
||||
try:
|
||||
monitor_service.delete_messages_session(session_id=session_id)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/transactions", response_model=List[TransactionModelResponse])
|
||||
async def get_transactions(
|
||||
source: Optional[str] = Query(None),
|
||||
|
|
|
|||
|
|
@ -15,10 +15,25 @@ import shlex
|
|||
from collections import OrderedDict, namedtuple
|
||||
from http.cookies import SimpleCookie
|
||||
|
||||
from uncurl.api import parser # type: ignore
|
||||
|
||||
parser.add_argument("-x", "--proxy", default={})
|
||||
parser.add_argument("-U", "--proxy-user", default="")
|
||||
ParsedArgs = namedtuple(
|
||||
"ParsedContext",
|
||||
[
|
||||
"command",
|
||||
"url",
|
||||
"data",
|
||||
"data_binary",
|
||||
"method",
|
||||
"headers",
|
||||
"compressed",
|
||||
"insecure",
|
||||
"user",
|
||||
"include",
|
||||
"silent",
|
||||
"proxy",
|
||||
"proxy_user",
|
||||
"cookies",
|
||||
],
|
||||
)
|
||||
|
||||
ParsedContext = namedtuple("ParsedContext", ["method", "url", "data", "headers", "cookies", "verify", "auth", "proxy"])
|
||||
|
||||
|
|
@ -27,24 +42,89 @@ def normalize_newlines(multiline_text):
|
|||
return multiline_text.replace(" \\\n", " ")
|
||||
|
||||
|
||||
def parse_curl_command(curl_command):
|
||||
tokens = shlex.split(normalize_newlines(curl_command))
|
||||
tokens = [token for token in tokens if token and token != " "]
|
||||
if "curl" not in tokens[0]:
|
||||
raise ValueError("Invalid curl command")
|
||||
args_template = {
|
||||
"command": None,
|
||||
"url": None,
|
||||
"data": None,
|
||||
"data_binary": None,
|
||||
"method": "get",
|
||||
"headers": [],
|
||||
"compressed": False,
|
||||
"insecure": False,
|
||||
"user": (),
|
||||
"include": False,
|
||||
"silent": False,
|
||||
"proxy": None,
|
||||
"proxy_user": None,
|
||||
"cookies": {},
|
||||
}
|
||||
args = args_template.copy()
|
||||
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
token = tokens[i]
|
||||
if token == "-X":
|
||||
i += 1
|
||||
args["method"] = tokens[i].lower()
|
||||
elif token in ("-d", "--data"):
|
||||
i += 1
|
||||
args["data"] = tokens[i]
|
||||
args["method"] = "post"
|
||||
elif token in ("-b", "--data-binary", "--data-raw"):
|
||||
i += 1
|
||||
args["data_binary"] = tokens[i]
|
||||
args["method"] = "post"
|
||||
elif token in ("-H", "--header"):
|
||||
i += 1
|
||||
args["headers"].append(tokens[i])
|
||||
elif token == "--compressed":
|
||||
args["compressed"] = True
|
||||
elif token in ("-k", "--insecure"):
|
||||
args["insecure"] = True
|
||||
elif token in ("-u", "--user"):
|
||||
i += 1
|
||||
args["user"] = tuple(tokens[i].split(":"))
|
||||
elif token in ("-I", "--include"):
|
||||
args["include"] = True
|
||||
elif token in ("-s", "--silent"):
|
||||
args["silent"] = True
|
||||
elif token in ("-x", "--proxy"):
|
||||
i += 1
|
||||
args["proxy"] = tokens[i]
|
||||
elif token in ("-U", "--proxy-user"):
|
||||
i += 1
|
||||
args["proxy_user"] = tokens[i]
|
||||
elif not token.startswith("-"):
|
||||
if args["command"] is None:
|
||||
args["command"] = token
|
||||
else:
|
||||
args["url"] = token
|
||||
i += 1
|
||||
|
||||
return ParsedArgs(**args)
|
||||
|
||||
|
||||
def parse_context(curl_command):
|
||||
method = "get"
|
||||
|
||||
tokens = shlex.split(normalize_newlines(curl_command))
|
||||
tokens = [token for token in tokens if token and token != " "]
|
||||
parsed_args = parser.parse_args(tokens)
|
||||
parsed_args: ParsedArgs = parse_curl_command(curl_command)
|
||||
|
||||
post_data = parsed_args.data or parsed_args.data_binary
|
||||
if post_data:
|
||||
method = "post"
|
||||
|
||||
if parsed_args.X:
|
||||
method = parsed_args.X.lower()
|
||||
if parsed_args.method:
|
||||
method = parsed_args.method.lower()
|
||||
|
||||
cookie_dict = OrderedDict()
|
||||
quoted_headers = OrderedDict()
|
||||
|
||||
for curl_header in parsed_args.header:
|
||||
for curl_header in parsed_args.headers:
|
||||
if curl_header.startswith(":"):
|
||||
occurrence = [m.start() for m in re.finditer(":", curl_header)]
|
||||
header_key, header_value = curl_header[: occurrence[1]], curl_header[occurrence[1] + 1 :]
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ def read_text_file(file_path: str) -> str:
|
|||
with open(file_path, "rb") as f:
|
||||
raw_data = f.read()
|
||||
result = chardet.detect(raw_data)
|
||||
encoding = result['encoding']
|
||||
encoding = result["encoding"]
|
||||
|
||||
with open(file_path, "r", encoding=encoding) as f:
|
||||
return f.read()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import re
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import Input, Output, Text
|
||||
|
||||
|
||||
class CodeBlockExtractor(Component):
|
||||
display_name = "Code Block Extractor"
|
||||
description = "Extracts code block from text."
|
||||
|
||||
inputs = [Input(name="text", field_type=Text, description="Text to extract code blocks from.")]
|
||||
|
||||
outputs = [Output(name="code_block", display_name="Code Block", method="get_code_block")]
|
||||
|
||||
def get_code_block(self) -> Text:
|
||||
text = self.text.strip()
|
||||
# Extract code block
|
||||
# It may start with ``` or ```language
|
||||
# It may end with ```
|
||||
pattern = r"^```(?:\w+)?\s*\n(.*?)(?=^```)```"
|
||||
match = re.search(pattern, text, re.MULTILINE)
|
||||
code_block = ""
|
||||
if match:
|
||||
code_block = match.group(1)
|
||||
return code_block
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from .AstraDBSearch import AstraDBSearchComponent
|
||||
from .ChromaSearch import ChromaSearchComponent
|
||||
from .FAISSSearch import FAISSSearchComponent
|
||||
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
|
||||
from .PineconeSearch import PineconeSearchComponent
|
||||
from .QdrantSearch import QdrantSearchComponent
|
||||
from .RedisSearch import RedisSearchComponent
|
||||
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
|
||||
from .VectaraSearch import VectaraSearchComponent
|
||||
from .WeaviateSearch import WeaviateSearchVectorStore
|
||||
from .pgvectorSearch import PGVectorSearchComponent
|
||||
from .Couchbase import CouchbaseSearchComponent # type: ignore
|
||||
|
||||
__all__ = [
|
||||
"AstraDBSearchComponent",
|
||||
"ChromaSearchComponent",
|
||||
"CouchbaseSearchComponent",
|
||||
"FAISSSearchComponent",
|
||||
"MongoDBAtlasSearchComponent",
|
||||
"PineconeSearchComponent",
|
||||
"QdrantSearchComponent",
|
||||
"RedisSearchComponent",
|
||||
"SupabaseSearchComponent",
|
||||
"VectaraSearchComponent",
|
||||
"WeaviateSearchVectorStore",
|
||||
"PGVectorSearchComponent",
|
||||
]
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from .AstraDB import AstraDBVectorStoreComponent
|
||||
from .Chroma import ChromaComponent
|
||||
from .FAISS import FAISSComponent
|
||||
from .MongoDBAtlasVector import MongoDBAtlasComponent
|
||||
from .Pinecone import PineconeComponent
|
||||
from .Qdrant import QdrantComponent
|
||||
from .Redis import RedisComponent
|
||||
from .SupabaseVectorStore import SupabaseComponent
|
||||
from .Vectara import VectaraComponent
|
||||
from .Weaviate import WeaviateVectorStoreComponent
|
||||
from .pgvector import PGVectorComponent
|
||||
from .Couchbase import CouchbaseComponent
|
||||
|
||||
__all__ = [
|
||||
"AstraDBVectorStoreComponent",
|
||||
"ChromaComponent",
|
||||
"CouchbaseComponent",
|
||||
"FAISSComponent",
|
||||
"MongoDBAtlasComponent",
|
||||
"PineconeComponent",
|
||||
"QdrantComponent",
|
||||
"RedisComponent",
|
||||
"SupabaseComponent",
|
||||
"VectaraComponent",
|
||||
"WeaviateVectorStoreComponent",
|
||||
"base",
|
||||
"PGVectorComponent",
|
||||
]
|
||||
|
|
@ -297,7 +297,7 @@ class CodeParser:
|
|||
bases = self.execute_and_inspect_classes(self.code)
|
||||
except Exception as e:
|
||||
# If the code cannot be executed, return an empty list
|
||||
logger.exception(e)
|
||||
logger.debug(e)
|
||||
bases = []
|
||||
raise e
|
||||
return bases
|
||||
|
|
|
|||
|
|
@ -71,13 +71,13 @@ class Component(CustomComponent):
|
|||
for output in self.outputs:
|
||||
# Build the output if it's connected to some other vertex
|
||||
# or if it's not connected to any vertex
|
||||
if not vertex.outgoing_edges or output.display_name in vertex.edges_source_names:
|
||||
if not vertex.outgoing_edges or output.name in vertex.edges_source_names:
|
||||
method: Callable | Awaitable = getattr(self, output.method)
|
||||
result = method()
|
||||
# If the method is asynchronous, we need to await it
|
||||
if inspect.iscoroutinefunction(method):
|
||||
result = await result
|
||||
_results[output.display_name] = result
|
||||
_results[output.name] = result
|
||||
self._results = _results
|
||||
return _results
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ class DirectoryReader:
|
|||
component_tuple = (*build_component(component), component)
|
||||
components.append(component_tuple)
|
||||
except Exception as e:
|
||||
logger.error(f"Error while loading component { component['name']}: {e}")
|
||||
logger.debug(f"Error while loading component { component['name']}")
|
||||
logger.debug(e)
|
||||
continue
|
||||
items.append({"name": menu["name"], "path": menu["path"], "components": components})
|
||||
filtered = [menu for menu in items if menu["components"]]
|
||||
|
|
@ -265,8 +266,7 @@ class DirectoryReader:
|
|||
if validation_result:
|
||||
try:
|
||||
output_types = self.get_output_types_from_code(result_content)
|
||||
except Exception as exc:
|
||||
logger.exception(f"Error while getting output types from code: {str(exc)}")
|
||||
except Exception:
|
||||
output_types = [component_name_camelcase]
|
||||
else:
|
||||
output_types = [component_name_camelcase]
|
||||
|
|
|
|||
|
|
@ -29,18 +29,26 @@ from .constants import (
|
|||
from .range_spec import RangeSpec
|
||||
|
||||
|
||||
def _import_template_field():
|
||||
def _import_input_class():
|
||||
from langflow.template.field.base import Input
|
||||
|
||||
return Input
|
||||
|
||||
|
||||
def _import_output_class():
|
||||
from langflow.template.field.base import Output
|
||||
|
||||
return Output
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
# This is to avoid circular imports
|
||||
if name == "Input":
|
||||
return _import_template_field()
|
||||
return _import_input_class()
|
||||
elif name == "RangeSpec":
|
||||
return RangeSpec
|
||||
elif name == "Output":
|
||||
return _import_output_class()
|
||||
# The other names should work as if they were imported from constants
|
||||
# Import the constants module langflow.field_typing.constants
|
||||
from . import constants
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain.memory.chat_memory import BaseChatMemory
|
|||
from langchain_core.document_loaders import BaseLoader
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.language_models import BaseLLM, BaseLanguageModel
|
||||
from langchain_core.language_models import BaseLanguageModel, BaseLLM
|
||||
from langchain_core.memory import BaseMemory
|
||||
from langchain_core.output_parsers import BaseOutputParser
|
||||
from langchain_core.prompts import BasePromptTemplate, ChatPromptTemplate, PromptTemplate
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class InterfaceVertex(ComponentVertex):
|
|||
if isinstance(message, str):
|
||||
message = unescape_string(message)
|
||||
stream_url = None
|
||||
text_output = self.results["Message"]
|
||||
text_output = self.results["message"]
|
||||
if isinstance(text_output, (AIMessage, AIMessageChunk)):
|
||||
artifacts = ChatOutputResponse.from_message(
|
||||
text_output,
|
||||
|
|
@ -173,8 +173,8 @@ class InterfaceVertex(ComponentVertex):
|
|||
elif isinstance(message, (AsyncIterator, Iterator)):
|
||||
stream_url = self.build_stream_url()
|
||||
message = ""
|
||||
self.results["Message"] = message
|
||||
self.results["Record"].message = message
|
||||
self.results["message"] = message
|
||||
self.results["record"].message = message
|
||||
self._built_object = self.results
|
||||
elif not isinstance(text_output, str):
|
||||
message = str(text_output)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,9 @@ async def run_flow(
|
|||
|
||||
fallback_to_env_vars = get_settings_service().settings.fallback_to_env_var
|
||||
|
||||
return await graph.arun(inputs_list, inputs_components=inputs_components, types=types, fallback_to_env_vars=fallback_to_env_vars)
|
||||
return await graph.arun(
|
||||
inputs_list, inputs_components=inputs_components, types=types, fallback_to_env_vars=fallback_to_env_vars
|
||||
)
|
||||
|
||||
|
||||
def generate_function_for_flow(
|
||||
|
|
@ -249,7 +251,7 @@ def get_flow_by_id_or_endpoint_name(
|
|||
flow = db.get(Flow, flow_id)
|
||||
except ValueError:
|
||||
endpoint_name = flow_id_or_name
|
||||
stmt = select(Flow).where(Flow.name == endpoint_name)
|
||||
stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)
|
||||
if user_id:
|
||||
stmt = stmt.where(Flow.user_id == user_id)
|
||||
flow = db.exec(stmt).first()
|
||||
|
|
|
|||
|
|
@ -599,6 +599,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -607,6 +608,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
@ -775,6 +777,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -783,6 +786,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,6 +459,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -467,6 +468,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -439,6 +440,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
@ -631,6 +633,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -639,6 +642,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -164,6 +165,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
@ -356,6 +358,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -364,6 +367,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,6 +437,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -445,6 +446,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
@ -634,6 +636,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -642,6 +645,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
@ -755,6 +759,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Text",
|
||||
"name": "text",
|
||||
"method": "text_response"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -162,6 +163,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
@ -1454,6 +1456,7 @@
|
|||
"Text"
|
||||
],
|
||||
"selected": "Text",
|
||||
"display_name": "Message",
|
||||
"name": "message",
|
||||
"method": "text_response"
|
||||
},
|
||||
|
|
@ -1462,6 +1465,7 @@
|
|||
"Record"
|
||||
],
|
||||
"selected": "Record",
|
||||
"display_name": "Record",
|
||||
"name": "record",
|
||||
"method": "record_response"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from .load import load_flow_from_json, run_flow_from_json # noqa: F401
|
||||
from .load import load_flow_from_json, run_flow_from_json
|
||||
from .utils import upload_file, get_flow
|
||||
|
||||
__all__ = ["load_flow_from_json", "run_flow_from_json"]
|
||||
__all__ = ["load_flow_from_json", "run_flow_from_json", "upload_file", "get_flow"]
|
||||
|
|
|
|||
89
src/backend/base/langflow/load/utils.py
Normal file
89
src/backend/base/langflow/load/utils.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import httpx
|
||||
|
||||
from langflow.services.database.models.flow.model import FlowBase
|
||||
|
||||
|
||||
def upload(file_path, host, flow_id):
|
||||
"""
|
||||
Upload a file to Langflow and return the file path.
|
||||
|
||||
Args:
|
||||
file_path (str): The path to the file to be uploaded.
|
||||
host (str): The host URL of Langflow.
|
||||
flow_id (UUID): The ID of the flow to which the file belongs.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the file path.
|
||||
|
||||
Raises:
|
||||
Exception: If an error occurs during the upload process.
|
||||
"""
|
||||
try:
|
||||
url = f"{host}/api/v1/upload/{flow_id}"
|
||||
response = httpx.post(url, files={"file": open(file_path, "rb")})
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
raise Exception(f"Error uploading file: {response.status_code}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Error uploading file: {e}")
|
||||
|
||||
|
||||
def upload_file(file_path, host, flow_id, components, tweaks={}):
|
||||
"""
|
||||
Upload a file to Langflow and return the file path.
|
||||
|
||||
Args:
|
||||
file_path (str): The path to the file to be uploaded.
|
||||
host (str): The host URL of Langflow.
|
||||
port (int): The port number of Langflow.
|
||||
flow_id (UUID): The ID of the flow to which the file belongs.
|
||||
components (str): List of component IDs or names that need the file.
|
||||
tweaks (dict): A dictionary of tweaks to be applied to the file.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the file path and any tweaks that were applied.
|
||||
|
||||
Raises:
|
||||
Exception: If an error occurs during the upload process.
|
||||
"""
|
||||
try:
|
||||
response = upload(file_path, host, flow_id)
|
||||
if response["file_path"]:
|
||||
for component in components:
|
||||
if isinstance(component, str):
|
||||
tweaks[component] = {"file_path": response["file_path"]}
|
||||
else:
|
||||
raise ValueError(f"Component ID or name must be a string. Got {type(component)}")
|
||||
return tweaks
|
||||
else:
|
||||
raise ValueError("Error uploading file")
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error uploading file: {e}")
|
||||
|
||||
|
||||
def get_flow(url: str, flow_id: str):
|
||||
"""Get the details of a flow from Langflow.
|
||||
|
||||
Args:
|
||||
url (str): The host URL of Langflow.
|
||||
port (int): The port number of Langflow.
|
||||
flow_id (UUID): The ID of the flow to retrieve.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the details of the flow.
|
||||
|
||||
Raises:
|
||||
Exception: If an error occurs during the retrieval process.
|
||||
"""
|
||||
try:
|
||||
flow_url = f"{url}/api/v1/flows/{flow_id}"
|
||||
response = httpx.get(flow_url)
|
||||
if response.status_code == 200:
|
||||
json_response = response.json()
|
||||
flow = FlowBase(**json_response).model_dump()
|
||||
return flow
|
||||
else:
|
||||
raise Exception(f"Error retrieving flow: {response.status_code}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Error retrieving flow: {e}")
|
||||
|
|
@ -59,7 +59,7 @@ async def run_graph_internal(
|
|||
outputs or [],
|
||||
stream=stream,
|
||||
session_id=session_id_str or "",
|
||||
fallback_to_env_vars=fallback_to_env_vars
|
||||
fallback_to_env_vars=fallback_to_env_vars,
|
||||
)
|
||||
if session_id_str and session_service:
|
||||
await session_service.update_session(session_id_str, (graph, artifacts))
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class ApiKeyRead(ApiKeyBase):
|
|||
id: UUID
|
||||
api_key: str = Field(schema_extra={"validate_default": True})
|
||||
user_id: UUID = Field()
|
||||
created_at: datetime = Field()
|
||||
|
||||
@field_validator("api_key")
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ class FlowBase(SQLModel):
|
|||
is_component: Optional[bool] = Field(default=False, nullable=True)
|
||||
updated_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(timezone.utc), nullable=True)
|
||||
webhook: Optional[bool] = Field(default=False, nullable=True, description="Can be used on the webhook endpoint")
|
||||
folder_id: Optional[UUID] = Field(default=None, nullable=True)
|
||||
endpoint_name: Optional[str] = Field(default=None, nullable=True, index=True)
|
||||
|
||||
@field_validator("endpoint_name")
|
||||
|
|
|
|||
|
|
@ -122,6 +122,13 @@ class MessageModelResponse(MessageModel):
|
|||
return v
|
||||
|
||||
|
||||
class MessageModelRequest(MessageModel):
|
||||
message: str = Field(default="")
|
||||
sender: str = Field(default="")
|
||||
sender_name: str = Field(default="")
|
||||
session_id: str = Field(default="")
|
||||
|
||||
|
||||
class VertexBuildModel(BaseModel):
|
||||
index: Optional[int] = Field(default=None, alias="index", exclude=True)
|
||||
id: Optional[str] = Field(default=None, alias="id")
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ class MonitorService(Service):
|
|||
except Exception as e:
|
||||
logger.exception(f"Error initializing monitor service: {e}")
|
||||
|
||||
def exec_query(self, query: str):
|
||||
with duckdb.connect(str(self.db_path)) as conn:
|
||||
return conn.execute(query).df()
|
||||
|
||||
def to_df(self, table_name):
|
||||
return self.load_table_as_dataframe(table_name)
|
||||
|
||||
|
|
@ -69,7 +73,7 @@ class MonitorService(Service):
|
|||
valid: Optional[bool] = None,
|
||||
order_by: Optional[str] = "timestamp",
|
||||
):
|
||||
query = "SELECT index,flow_id, valid, params, data, artifacts, timestamp FROM vertex_builds"
|
||||
query = "SELECT id, index,flow_id, valid, params, data, artifacts, timestamp FROM vertex_builds"
|
||||
conditions = []
|
||||
if flow_id:
|
||||
conditions.append(f"flow_id = '{flow_id}'")
|
||||
|
|
@ -98,11 +102,22 @@ class MonitorService(Service):
|
|||
with duckdb.connect(str(self.db_path)) as conn:
|
||||
conn.execute(query)
|
||||
|
||||
def delete_messages(self, session_id: str):
|
||||
def delete_messages_session(self, session_id: str):
|
||||
query = f"DELETE FROM messages WHERE session_id = '{session_id}'"
|
||||
|
||||
with duckdb.connect(str(self.db_path)) as conn:
|
||||
conn.execute(query)
|
||||
return self.exec_query(query)
|
||||
|
||||
def delete_messages(self, message_ids: list[int]):
|
||||
query = f"DELETE FROM messages WHERE index IN ({','.join(map(str, message_ids))})"
|
||||
|
||||
return self.exec_query(query)
|
||||
|
||||
def update_message(self, message_id: int, **kwargs):
|
||||
query = (
|
||||
f"""UPDATE messages SET {', '.join(f"{k} = '{v}'" for k, v in kwargs.items())} WHERE index = {message_id}"""
|
||||
)
|
||||
|
||||
return self.exec_query(query)
|
||||
|
||||
def add_message(self, message: MessageModel):
|
||||
self.add_row("messages", message)
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ class Settings(BaseSettings):
|
|||
langchain_cache: str = "InMemoryCache"
|
||||
load_flows_path: Optional[str] = None
|
||||
|
||||
|
||||
# Redis
|
||||
redis_host: str = "localhost"
|
||||
redis_port: int = 6379
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class Input(BaseModel):
|
|||
def serialize_model(self, handler):
|
||||
result = handler(self)
|
||||
# If the field is str, we add the Text input type
|
||||
if self.field_type in [str, Text]:
|
||||
if self.field_type in ["str", "Text"]:
|
||||
if "input_types" not in result:
|
||||
result["input_types"] = ["Text"]
|
||||
if self.field_type == Text:
|
||||
|
|
@ -110,7 +110,10 @@ class Input(BaseModel):
|
|||
# this should be done for all types
|
||||
# How to check if v is a type?
|
||||
if isinstance(v, (type, GenericAlias)):
|
||||
return str(v)
|
||||
if isinstance(v, type):
|
||||
v = v.__name__
|
||||
else:
|
||||
v = str(v)
|
||||
elif not isinstance(v, str):
|
||||
raise ValueError(f"type must be a string or a type, not {type(v)}")
|
||||
return v
|
||||
|
|
@ -144,19 +147,19 @@ class Input(BaseModel):
|
|||
|
||||
|
||||
class Output(BaseModel):
|
||||
types: Optional[list[str]] = Field(default=[], serialization_alias="types")
|
||||
types: Optional[list[str]] = Field(default=[])
|
||||
"""List of output types for the field."""
|
||||
|
||||
selected: Optional[str] = Field(default=None, serialization_alias="selected")
|
||||
selected: Optional[str] = Field(default=None)
|
||||
"""The selected output type for the field."""
|
||||
|
||||
name: str = Field(default=None)
|
||||
"""The name of the field."""
|
||||
|
||||
display_name: Optional[str] = Field(default=None)
|
||||
"""The display name of the field."""
|
||||
|
||||
name: str = Field(default=None, serialization_alias="name")
|
||||
"""The name of the field."""
|
||||
|
||||
method: Optional[str] = Field(default=None, serialization_alias="method")
|
||||
method: Optional[str] = Field(default=None)
|
||||
"""The method to use for the output."""
|
||||
|
||||
def to_dict(self):
|
||||
|
|
|
|||
57
src/backend/base/poetry.lock
generated
57
src/backend/base/poetry.lock
generated
|
|
@ -1159,13 +1159,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "langchain"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain-0.2.2-py3-none-any.whl", hash = "sha256:58ca0c47bcdd156da66f50a0a4fcedc49bf6950827f4a6b06c8c4842d55805f3"},
|
||||
{file = "langchain-0.2.2.tar.gz", hash = "sha256:9d61e50e9cdc2bea659bc5e6c03650ba048fda63a307490ae368e539f61a0d3a"},
|
||||
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"},
|
||||
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1181,29 +1181,15 @@ requests = ">=2,<3"
|
|||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
|
||||
[package.extras]
|
||||
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
|
||||
clarifai = ["clarifai (>=9.1.0)"]
|
||||
cli = ["typer (>=0.9.0,<0.10.0)"]
|
||||
cohere = ["cohere (>=4,<6)"]
|
||||
docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"]
|
||||
embeddings = ["sentence-transformers (>=2,<3)"]
|
||||
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
|
||||
javascript = ["esprima (>=4.0.1,<5.0.0)"]
|
||||
llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"]
|
||||
openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"]
|
||||
qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"]
|
||||
text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-community"
|
||||
version = "0.2.2"
|
||||
version = "0.2.4"
|
||||
description = "Community contributed LangChain integrations."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_community-0.2.2-py3-none-any.whl", hash = "sha256:470ee16e05f1acacb91a656b6d3c2cbf6fb6a8dcb00a13901cd1353cd29c2bb3"},
|
||||
{file = "langchain_community-0.2.2.tar.gz", hash = "sha256:fb09faf4640726a929932056dc55ff120e490aaf2e424fae8ddbb15605195447"},
|
||||
{file = "langchain_community-0.2.4-py3-none-any.whl", hash = "sha256:8582e9800f4837660dc297cccd2ee1ddc1d8c440d0fe8b64edb07620f0373b0e"},
|
||||
{file = "langchain_community-0.2.4.tar.gz", hash = "sha256:2bb6a1a36b8500a564d25d76469c02457b1a7c3afea6d4a609a47c06b993e3e4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1218,19 +1204,15 @@ requests = ">=2,<3"
|
|||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
|
||||
[package.extras]
|
||||
cli = ["typer (>=0.9.0,<0.10.0)"]
|
||||
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "azure-identity (>=1.15.0,<2.0.0)", "azure-search-documents (==11.4.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.6,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpathlib (>=0.18,<0.19)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "httpx-sse (>=0.4.0,<0.5.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "oracledb (>=2.2.0,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "premai (>=0.3.25,<0.4.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pyjwt (>=2.8.0,<3.0.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "simsimd (>=4.3.1,<5.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "vdms (>=0.0.20,<0.0.21)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.2.4-py3-none-any.whl", hash = "sha256:5212f7ec78a525e88a178ed3aefe2fd7134b03fb92573dfbab9914f1d92d6ec5"},
|
||||
{file = "langchain_core-0.2.4.tar.gz", hash = "sha256:82bdcc546eb0341cefcf1f4ecb3e49836fff003903afddda2d1312bb8491ef81"},
|
||||
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"},
|
||||
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1241,9 +1223,6 @@ pydantic = ">=1,<3"
|
|||
PyYAML = ">=5.3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
|
||||
[package.extras]
|
||||
extended-testing = ["jinja2 (>=3,<4)"]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-experimental"
|
||||
version = "0.0.60"
|
||||
|
|
@ -1296,13 +1275,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
version = "0.1.71"
|
||||
version = "0.1.75"
|
||||
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langsmith-0.1.71-py3-none-any.whl", hash = "sha256:a9979de2780442eb24eced31314e49f5ece6f807a0d70740b2c6c39217226794"},
|
||||
{file = "langsmith-0.1.71.tar.gz", hash = "sha256:bdb1037a08acf7c19b3969c085df09c1eecb65baca8400b3b76ae871e2c8a97e"},
|
||||
{file = "langsmith-0.1.75-py3-none-any.whl", hash = "sha256:d08b08dd6b3fa4da170377f95123d77122ef4c52999d10fff4ae08ff70d07aed"},
|
||||
{file = "langsmith-0.1.75.tar.gz", hash = "sha256:61274e144ea94c297dd78ce03e6dfae18459fe9bd8ab5094d61a0c4816561279"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1600,13 +1579,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "marshmallow"
|
||||
version = "3.21.2"
|
||||
version = "3.21.3"
|
||||
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"},
|
||||
{file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"},
|
||||
{file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"},
|
||||
{file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2214,13 +2193,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
|
|||
|
||||
[[package]]
|
||||
name = "pydantic-settings"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
description = "Settings management using Pydantic"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_settings-2.3.0-py3-none-any.whl", hash = "sha256:26eeed27370a9c5e3f64e4a7d6602573cbedf05ed940f1d5b11c3f178427af7a"},
|
||||
{file = "pydantic_settings-2.3.0.tar.gz", hash = "sha256:78db28855a71503cfe47f39500a1dece523c640afd5280edb5c5c9c9cfa534c9"},
|
||||
{file = "pydantic_settings-2.3.1-py3-none-any.whl", hash = "sha256:acb2c213140dfff9669f4fe9f8180d43914f51626db28ab2db7308a576cce51a"},
|
||||
{file = "pydantic_settings-2.3.1.tar.gz", hash = "sha256:e34bbd649803a6bb3e2f0f58fb0edff1f0c7f556849fda106cc21bcce12c30ab"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow-base"
|
||||
version = "0.0.56"
|
||||
version = "0.0.60"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Langflow <contact@langflow.org>"]
|
||||
maintainers = [
|
||||
|
|
|
|||
53
src/frontend/package-lock.json
generated
53
src/frontend/package-lock.json
generated
|
|
@ -26,6 +26,7 @@
|
|||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.6",
|
||||
"@tabler/icons-react": "^2.32.0",
|
||||
"@tailwindcss/forms": "^0.5.6",
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
"cmdk": "^1.0.0",
|
||||
"dompurify": "^3.0.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"emoji-regex": "^10.3.0",
|
||||
"esbuild": "^0.17.19",
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^11.0.6",
|
||||
|
|
@ -50,6 +52,7 @@
|
|||
"million": "^3.0.6",
|
||||
"moment": "^2.29.4",
|
||||
"openseadragon": "^4.1.1",
|
||||
"p-debounce": "^4.0.0",
|
||||
"playwright": "^1.42.0",
|
||||
"react": "^18.2.21",
|
||||
"react-ace": "^10.1.0",
|
||||
|
|
@ -2761,6 +2764,31 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toggle": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz",
|
||||
"integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tooltip": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz",
|
||||
|
|
@ -5965,9 +5993,9 @@
|
|||
"integrity": "sha512-C6q/xcUJf/2yODRxAVCfIk4j3y3LMsD0ehiE2RQNV2cxc8XU62gR6vvYh3+etSUzlgTfil+qDHI1vubpdf0TOA=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
|
||||
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
|
|
@ -10017,6 +10045,15 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-debounce": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-4.0.0.tgz",
|
||||
"integrity": "sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/p-finally": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||
|
|
@ -12172,6 +12209,16 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/string-width/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.6",
|
||||
"@tabler/icons-react": "^2.32.0",
|
||||
"@tailwindcss/forms": "^0.5.6",
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
"cmdk": "^1.0.0",
|
||||
"dompurify": "^3.0.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"emoji-regex": "^10.3.0",
|
||||
"esbuild": "^0.17.19",
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^11.0.6",
|
||||
|
|
@ -45,6 +47,7 @@
|
|||
"million": "^3.0.6",
|
||||
"moment": "^2.29.4",
|
||||
"openseadragon": "^4.1.1",
|
||||
"p-debounce": "^4.0.0",
|
||||
"playwright": "^1.42.0",
|
||||
"react": "^18.2.21",
|
||||
"react-ace": "^10.1.0",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
|||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
fullyParallel: false,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
|
|
@ -52,18 +52,18 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: {
|
||||
...devices["Desktop Firefox"],
|
||||
launchOptions: {
|
||||
firefoxUserPrefs: {
|
||||
"dom.events.asyncClipboard.readText": true,
|
||||
"dom.events.testing.asyncClipboard": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: "firefox",
|
||||
// use: {
|
||||
// ...devices["Desktop Firefox"],
|
||||
// launchOptions: {
|
||||
// firefoxUserPrefs: {
|
||||
// "dom.events.asyncClipboard.readText": true,
|
||||
// "dom.events.testing.asyncClipboard": true,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
],
|
||||
webServer: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -164,3 +164,13 @@ body {
|
|||
.ag-body-vertical-scroll-viewport::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #bbb;
|
||||
}
|
||||
|
||||
/* This CSS is to not apply the border for the column having 'no-border' class */
|
||||
.no-border.ag-cell:focus {
|
||||
border: none !important;
|
||||
outline: none;
|
||||
}
|
||||
.no-border.ag-cell {
|
||||
border: none !important;
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import axios from "axios";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
|
@ -222,12 +221,19 @@ export default function App() {
|
|||
id={alert.id}
|
||||
removeAlert={removeAlert}
|
||||
/>
|
||||
) : alert.type === "notice" ? (
|
||||
<NoticeAlert
|
||||
key={alert.id}
|
||||
title={alert.title}
|
||||
link={alert.link}
|
||||
id={alert.id}
|
||||
removeAlert={removeAlert}
|
||||
/>
|
||||
) : (
|
||||
alert.type === "notice" && (
|
||||
<NoticeAlert
|
||||
alert.type === "success" && (
|
||||
<SuccessAlert
|
||||
key={alert.id}
|
||||
title={alert.title}
|
||||
link={alert.link}
|
||||
id={alert.id}
|
||||
removeAlert={removeAlert}
|
||||
/>
|
||||
|
|
@ -236,20 +242,6 @@ export default function App() {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="z-40 flex flex-col-reverse">
|
||||
{tempNotificationList.map((alert) => (
|
||||
<div key={alert.id}>
|
||||
{alert.type === "success" && (
|
||||
<SuccessAlert
|
||||
key={alert.id}
|
||||
title={alert.title}
|
||||
id={alert.id}
|
||||
removeAlert={removeAlert}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ export default function ParameterComponent({
|
|||
});
|
||||
}}
|
||||
name={name}
|
||||
data={data}
|
||||
data={data.node?.template[name]}
|
||||
/>
|
||||
</div>
|
||||
{data.node?.template[name]?.refresh_button && (
|
||||
|
|
@ -442,8 +442,8 @@ export default function ParameterComponent({
|
|||
data.node?.template[name]?.real_time_refresh)
|
||||
}
|
||||
>
|
||||
<div className="mt-2 flex w-full items-center">
|
||||
<div className="w-5/6 flex-grow">
|
||||
<div className="mt-2 flex w-full items-center gap-2">
|
||||
<div className="flex-1">
|
||||
<Dropdown
|
||||
disabled={disabled}
|
||||
isLoading={isLoading}
|
||||
|
|
@ -461,7 +461,6 @@ export default function ParameterComponent({
|
|||
name={name}
|
||||
data={data}
|
||||
button_text={data.node?.template[name]?.refresh_button_text}
|
||||
className="extra-side-bar-buttons ml-2 mt-1"
|
||||
handleUpdateValues={handleRefreshButtonPress}
|
||||
id={"refresh-button-" + name}
|
||||
/>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import emojiRegex from "emoji-regex";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
||||
|
|
@ -55,14 +56,14 @@ export default function GenericNode({
|
|||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
||||
const [inputDescription, setInputDescription] = useState(false);
|
||||
const [nodeDescription, setNodeDescription] = useState(
|
||||
data.node?.description!
|
||||
data.node?.description!,
|
||||
);
|
||||
const [isOutdated, setIsOutdated] = useState(false);
|
||||
const buildStatus = useFlowStore(
|
||||
(state) => state.flowBuildStatus[data.id]?.status
|
||||
(state) => state.flowBuildStatus[data.id]?.status,
|
||||
);
|
||||
const lastRunTime = useFlowStore(
|
||||
(state) => state.flowBuildStatus[data.id]?.timestamp
|
||||
(state) => state.flowBuildStatus[data.id]?.timestamp,
|
||||
);
|
||||
const [validationStatus, setValidationStatus] =
|
||||
useState<validationStatusType | null>(null);
|
||||
|
|
@ -77,10 +78,10 @@ export default function GenericNode({
|
|||
// first check if data.type in NATIVE_CATEGORIES
|
||||
// if not return
|
||||
if (!data.node?.template?.code?.value) return;
|
||||
const thisNodeTemplate = templates[data.type].template;
|
||||
const thisNodeTemplate = templates[data.type]?.template;
|
||||
// if the template does not have a code key
|
||||
// return
|
||||
if (!thisNodeTemplate.code) return;
|
||||
if (!thisNodeTemplate?.code) return;
|
||||
const currentCode = thisNodeTemplate.code?.value;
|
||||
const thisNodesCode = data.node!.template?.code?.value;
|
||||
const componentsToIgnore = ["Custom Component"];
|
||||
|
|
@ -115,7 +116,7 @@ export default function GenericNode({
|
|||
|
||||
updateNodeInternals(data.id);
|
||||
},
|
||||
[data.id, data.node, setNode, setIsOutdated]
|
||||
[data.id, data.node, setNode, setIsOutdated],
|
||||
);
|
||||
|
||||
if (!data.node!.template) {
|
||||
|
|
@ -220,8 +221,7 @@ export default function GenericNode({
|
|||
|
||||
const nameEditable = true;
|
||||
|
||||
const emojiRegex = /\p{Emoji}/u;
|
||||
const isEmoji = emojiRegex.test(data?.node?.icon!);
|
||||
const isEmoji = emojiRegex().test(data?.node?.icon!);
|
||||
|
||||
const iconNodeRender = useCallback(() => {
|
||||
const iconElement = data?.node?.icon;
|
||||
|
|
@ -255,8 +255,17 @@ export default function GenericNode({
|
|||
const isDark = useDarkStore((state) => state.dark);
|
||||
const renderIconStatus = (
|
||||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: validationStatusType | null
|
||||
validationStatus: validationStatusType | null,
|
||||
) => {
|
||||
const conditionSuccess = validationStatus && validationStatus.valid;
|
||||
const conditionInactive =
|
||||
validationStatus &&
|
||||
!validationStatus.valid &&
|
||||
buildStatus === BuildStatus.INACTIVE;
|
||||
const conditionError =
|
||||
buildStatus === BuildStatus.ERROR ||
|
||||
(validationStatus && !validationStatus.valid);
|
||||
|
||||
if (buildStatus === BuildStatus.BUILDING) {
|
||||
return <Loading className="text-medium-indigo" />;
|
||||
} else {
|
||||
|
|
@ -264,31 +273,30 @@ export default function GenericNode({
|
|||
<>
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
|
||||
className={cn(
|
||||
!conditionSuccess && !conditionInactive && !conditionError
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
"absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground transition-all group-hover:text-medium-indigo group-hover/node:opacity-100",
|
||||
)}
|
||||
/>
|
||||
{validationStatus && validationStatus.valid ? (
|
||||
{conditionSuccess ? (
|
||||
<Checkmark
|
||||
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
|
||||
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover/node:opacity-0"
|
||||
isVisible={true}
|
||||
/>
|
||||
) : validationStatus &&
|
||||
!validationStatus.valid &&
|
||||
buildStatus === BuildStatus.INACTIVE ? (
|
||||
) : conditionInactive ? (
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-gray opacity-30 transition-all group-hover/node:opacity-0"
|
||||
/>
|
||||
) : buildStatus === BuildStatus.ERROR ||
|
||||
(validationStatus && !validationStatus.valid) ? (
|
||||
) : conditionError ? (
|
||||
<Xmark
|
||||
isVisible={true}
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover/node:opacity-0"
|
||||
/>
|
||||
) : (
|
||||
<IconComponent
|
||||
name="Play"
|
||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
|
||||
/>
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
@ -296,7 +304,7 @@ export default function GenericNode({
|
|||
};
|
||||
const getSpecificClassFromBuildStatus = (
|
||||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: validationStatusType | null
|
||||
validationStatus: validationStatusType | null,
|
||||
) => {
|
||||
let isInvalid = validationStatus && !validationStatus.valid;
|
||||
|
||||
|
|
@ -320,11 +328,11 @@ export default function GenericNode({
|
|||
selected: boolean,
|
||||
showNode: boolean,
|
||||
buildStatus: BuildStatus | undefined,
|
||||
validationStatus: validationStatusType | null
|
||||
validationStatus: validationStatusType | null,
|
||||
) => {
|
||||
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
||||
buildStatus,
|
||||
validationStatus
|
||||
validationStatus,
|
||||
);
|
||||
|
||||
const baseBorderClass = getBaseBorderClass(selected);
|
||||
|
|
@ -332,8 +340,8 @@ export default function GenericNode({
|
|||
const names = classNames(
|
||||
baseBorderClass,
|
||||
nodeSizeClass,
|
||||
"generic-node-div",
|
||||
specificClassFromBuildStatus
|
||||
"generic-node-div group/node",
|
||||
specificClassFromBuildStatus,
|
||||
);
|
||||
return names;
|
||||
};
|
||||
|
|
@ -393,7 +401,7 @@ export default function GenericNode({
|
|||
selected,
|
||||
showNode,
|
||||
buildStatus,
|
||||
validationStatus
|
||||
validationStatus,
|
||||
)}
|
||||
>
|
||||
{data.node?.beta && showNode && (
|
||||
|
|
@ -416,6 +424,7 @@ export default function GenericNode({
|
|||
"generic-node-title-arrangement rounded-full" +
|
||||
(!showNode && " justify-center ")
|
||||
}
|
||||
data-testid="generic-node-title-arrangement"
|
||||
>
|
||||
{iconNodeRender()}
|
||||
{showNode && (
|
||||
|
|
@ -452,7 +461,7 @@ export default function GenericNode({
|
|||
<div className="group flex items-start gap-1.5">
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div
|
||||
onDoubleClick={(event) => {
|
||||
onClick={(event) => {
|
||||
if (nameEditable) {
|
||||
setInputName(true);
|
||||
}
|
||||
|
|
@ -466,21 +475,6 @@ export default function GenericNode({
|
|||
{data.node?.display_name}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
{nameEditable && (
|
||||
<div
|
||||
onClick={(event) => {
|
||||
setInputName(true);
|
||||
takeSnapshot();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="PencilLine"
|
||||
className="hidden h-3 w-3 text-status-blue group-hover:block"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -538,7 +532,7 @@ export default function GenericNode({
|
|||
}
|
||||
title={getFieldTitle(
|
||||
data.node?.template!,
|
||||
templateField
|
||||
templateField,
|
||||
)}
|
||||
info={data.node?.template[templateField].info}
|
||||
name={templateField}
|
||||
|
|
@ -566,7 +560,7 @@ export default function GenericNode({
|
|||
proxy={data.node?.template[templateField].proxy}
|
||||
showNode={showNode}
|
||||
/>
|
||||
)
|
||||
),
|
||||
)}
|
||||
{/* <ParameterComponent
|
||||
index={0}
|
||||
|
|
@ -720,14 +714,14 @@ export default function GenericNode({
|
|||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"generic-node-desc-text truncate-multiline word-break-break-word",
|
||||
"generic-node-desc-text cursor-text truncate-multiline word-break-break-word",
|
||||
(data.node?.description === "" ||
|
||||
!data.node?.description) &&
|
||||
nameEditable
|
||||
? "font-light italic"
|
||||
: ""
|
||||
: "",
|
||||
)}
|
||||
onDoubleClick={(e) => {
|
||||
onClick={(e) => {
|
||||
setInputDescription(true);
|
||||
takeSnapshot();
|
||||
}}
|
||||
|
|
@ -787,13 +781,13 @@ export default function GenericNode({
|
|||
}
|
||||
title={getFieldTitle(
|
||||
data.node?.template!,
|
||||
templateField
|
||||
templateField,
|
||||
)}
|
||||
info={data.node?.template[templateField].info}
|
||||
name={templateField}
|
||||
tooltipTitle={
|
||||
data.node?.template[templateField].input_types?.join(
|
||||
"\n"
|
||||
"\n",
|
||||
) ?? data.node?.template[templateField].type
|
||||
}
|
||||
required={data.node!.template[templateField].required}
|
||||
|
|
@ -820,7 +814,7 @@ export default function GenericNode({
|
|||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||
"flex-max-width justify-center"
|
||||
"flex-max-width justify-center",
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
|
|
@ -39,7 +39,7 @@ const useFetchDataOnMount = (
|
|||
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
list: [responseError?.response?.data?.detail ?? "Unknown error"],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
|
@ -43,7 +43,9 @@ const useHandleOnNewValue = (
|
|||
let responseError = error as ResponseErrorTypeAPI;
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail.error ?? "Unknown error"],
|
||||
list: [
|
||||
responseError?.response?.data?.detail.error ?? "Unknown error",
|
||||
],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
|
@ -26,7 +26,7 @@ const useHandleRefreshButtonPress = (setIsLoading, setNode) => {
|
|||
|
||||
setErrorData({
|
||||
title: "Error while updating the Component",
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
list: [responseError?.response?.data?.detail ?? "Unknown error"],
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
|
|
@ -2,7 +2,7 @@ import { APITemplateType } from "../../types/api";
|
|||
|
||||
export default function getFieldTitle(
|
||||
template: APITemplateType,
|
||||
templateField: string,
|
||||
templateField: string
|
||||
): string {
|
||||
return template[templateField].display_name
|
||||
? template[templateField].display_name!
|
||||
|
|
@ -36,7 +36,7 @@ export default function AlertDropdown({
|
|||
}}
|
||||
>
|
||||
<PopoverTrigger>{children}</PopoverTrigger>
|
||||
<PopoverContent className="nocopy nopan nodelete nodrag noundo flex h-[500px] w-[500px] flex-col">
|
||||
<PopoverContent className="nocopy nowheel nopan nodelete nodrag noundo flex h-[500px] w-[500px] flex-col">
|
||||
<div className="text-md flex flex-row justify-between pl-3 font-medium text-foreground">
|
||||
Notifications
|
||||
<div className="flex gap-3 pr-3 ">
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default function ErrorAlert({
|
|||
removeAlert(id);
|
||||
}, 500);
|
||||
}}
|
||||
className="error-build-message nocopy nopan nodelete nodrag noundo"
|
||||
className="error-build-message nocopy nowheel nopan nodelete nodrag noundo"
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
|
|
@ -51,13 +51,15 @@ export default function ErrorAlert({
|
|||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="error-build-foreground">{title}</h3>
|
||||
<h3 className="error-build-foreground line-clamp-2">{title}</h3>
|
||||
{list?.length !== 0 &&
|
||||
list?.some((item) => item !== null && item !== undefined) ? (
|
||||
<div className="error-build-message-div">
|
||||
<ul className="error-build-message-list">
|
||||
{list.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
<li key={index} className="line-clamp-5">
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default function NoticeAlert({
|
|||
setShow(false);
|
||||
removeAlert(id);
|
||||
}}
|
||||
className="nocopy nopan nodelete nodrag noundo mt-6 w-96 rounded-md bg-info-background p-4 shadow-xl"
|
||||
className="nocopy nowheel nopan nodelete nodrag noundo mt-6 w-96 rounded-md bg-info-background p-4 shadow-xl"
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
|
|
@ -47,7 +47,7 @@ export default function NoticeAlert({
|
|||
/>
|
||||
</div>
|
||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||
<p className="text-sm text-info-foreground word-break-break-word">
|
||||
<p className="line-clamp-2 text-sm text-info-foreground word-break-break-word">
|
||||
{title}
|
||||
</p>
|
||||
<p className="mt-3 text-sm md:ml-6 md:mt-0">
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default function SuccessAlert({
|
|||
setShow(false);
|
||||
removeAlert(id);
|
||||
}}
|
||||
className="success-alert nocopy nopan nodelete nodrag noundo"
|
||||
className="success-alert nocopy nowheel nopan nodelete nodrag noundo"
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
|
|
@ -45,7 +45,7 @@ export default function SuccessAlert({
|
|||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<p className="success-alert-message">{title}</p>
|
||||
<p className="success-alert-message line-clamp-2">{title}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ export default function ImageViewer({ image }) {
|
|||
const fullPageButton = document.getElementById("full-page-button");
|
||||
|
||||
zoomInButton!.addEventListener("click", () =>
|
||||
viewer.viewport.zoomBy(1.2),
|
||||
viewer.viewport.zoomBy(1.2)
|
||||
);
|
||||
zoomOutButton!.addEventListener("click", () =>
|
||||
viewer.viewport.zoomBy(0.8),
|
||||
viewer.viewport.zoomBy(0.8)
|
||||
);
|
||||
homeButton!.addEventListener("click", () => viewer.viewport.goHome());
|
||||
fullPageButton!.addEventListener("click", () =>
|
||||
viewer.setFullScreen(true),
|
||||
viewer.setFullScreen(true)
|
||||
);
|
||||
|
||||
// Optionally, you can set additional viewer options here
|
||||
|
|
@ -47,16 +47,16 @@ export default function ImageViewer({ image }) {
|
|||
return () => {
|
||||
viewer.destroy();
|
||||
zoomInButton!.removeEventListener("click", () =>
|
||||
viewer.viewport.zoomBy(1.2),
|
||||
viewer.viewport.zoomBy(1.2)
|
||||
);
|
||||
zoomOutButton!.removeEventListener("click", () =>
|
||||
viewer.viewport.zoomBy(0.8),
|
||||
viewer.viewport.zoomBy(0.8)
|
||||
);
|
||||
homeButton!.removeEventListener("click", () =>
|
||||
viewer.viewport.goHome(),
|
||||
viewer.viewport.goHome()
|
||||
);
|
||||
fullPageButton!.removeEventListener("click", () =>
|
||||
viewer.setFullScreen(true),
|
||||
viewer.setFullScreen(true)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,18 @@ import {
|
|||
AccordionTrigger,
|
||||
} from "../../components/ui/accordion";
|
||||
import { AccordionComponentType } from "../../types/components";
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
export default function AccordionComponent({
|
||||
trigger,
|
||||
children,
|
||||
disabled,
|
||||
open = [],
|
||||
keyValue,
|
||||
sideBar,
|
||||
}: AccordionComponentType): JSX.Element {
|
||||
const [value, setValue] = useState(
|
||||
open.length === 0 ? "" : getOpenAccordion(),
|
||||
open.length === 0 ? "" : getOpenAccordion()
|
||||
);
|
||||
|
||||
function getOpenAccordion(): string {
|
||||
|
|
@ -29,7 +31,9 @@ export default function AccordionComponent({
|
|||
}
|
||||
|
||||
function handleClick(): void {
|
||||
value === "" ? setValue(keyValue!) : setValue("");
|
||||
if (!disabled) {
|
||||
value === "" ? setValue(keyValue!) : setValue("");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -38,16 +42,18 @@ export default function AccordionComponent({
|
|||
type="single"
|
||||
className="w-full"
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
onValueChange={!disabled ? setValue : () => {}}
|
||||
>
|
||||
<AccordionItem value={keyValue!} className="border-b">
|
||||
<AccordionTrigger
|
||||
onClick={() => {
|
||||
handleClick();
|
||||
}}
|
||||
className={
|
||||
sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3"
|
||||
}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3",
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
)}
|
||||
>
|
||||
{trigger}
|
||||
</AccordionTrigger>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { useTypesStore } from "../../stores/typesStore";
|
|||
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import InputComponent from "../inputComponent";
|
||||
import { Button } from "../ui/button";
|
||||
import { Input } from "../ui/input";
|
||||
import { Label } from "../ui/label";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
|
@ -24,19 +23,19 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
|||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const componentFields = useTypesStore((state) => state.ComponentFields);
|
||||
const unavaliableFields = new Set(
|
||||
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)),
|
||||
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields))
|
||||
);
|
||||
|
||||
const availableFields = () => {
|
||||
const fields = Array.from(componentFields).filter(
|
||||
(field) => !unavaliableFields.has(field),
|
||||
(field) => !unavaliableFields.has(field)
|
||||
);
|
||||
|
||||
return sortByName(fields);
|
||||
};
|
||||
|
||||
const addGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.addGlobalVariable,
|
||||
(state) => state.addGlobalVariable
|
||||
);
|
||||
|
||||
function handleSaveVariable() {
|
||||
|
|
@ -65,12 +64,17 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
|||
let responseError = error as ResponseErrorDetailAPI;
|
||||
setErrorData({
|
||||
title: "Error creating variable",
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
list: [responseError?.response?.data?.detail ?? "Unknown error"],
|
||||
});
|
||||
});
|
||||
}
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen} size="x-small">
|
||||
<BaseModal
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
size="x-small"
|
||||
onSubmit={handleSaveVariable}
|
||||
>
|
||||
<BaseModal.Header
|
||||
description={
|
||||
"This variable will be encrypted and will be available for you to use in any of your projects."
|
||||
|
|
@ -137,9 +141,9 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
|||
></InputComponent>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
<BaseModal.Footer>
|
||||
<Button onClick={handleSaveVariable}>Save Variable</Button>
|
||||
</BaseModal.Footer>
|
||||
<BaseModal.Footer
|
||||
submit={{ label: "Save Variable", dataTestId: "save-variable-btn" }}
|
||||
/>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import {
|
|||
import { Checkbox } from "../ui/checkbox";
|
||||
import { FormControl, FormField } from "../ui/form";
|
||||
import Loading from "../ui/loading";
|
||||
import { convertTestName } from "./utils/convert-test-name";
|
||||
import DragCardComponent from "./components/dragCardComponent";
|
||||
import { convertTestName } from "./utils/convert-test-name";
|
||||
|
||||
export default function CollectionCardComponent({
|
||||
data,
|
||||
|
|
@ -60,11 +60,11 @@ export default function CollectionCardComponent({
|
|||
const [loading, setLoading] = useState(false);
|
||||
const [loadingLike, setLoadingLike] = useState(false);
|
||||
const [liked_by_user, setLiked_by_user] = useState(
|
||||
data?.liked_by_user ?? false,
|
||||
data?.liked_by_user ?? false
|
||||
);
|
||||
const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0);
|
||||
const [downloads_count, setDownloads_count] = useState(
|
||||
data?.downloads_count ?? 0,
|
||||
data?.downloads_count ?? 0
|
||||
);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
|
|
@ -75,12 +75,12 @@ export default function CollectionCardComponent({
|
|||
const [openPlayground, setOpenPlayground] = useState(false);
|
||||
const [openDelete, setOpenDelete] = useState(false);
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId,
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false);
|
||||
|
||||
const selectedFlowsComponentsCards = useFlowsManagerStore(
|
||||
(state) => state.selectedFlowsComponentsCards,
|
||||
(state) => state.selectedFlowsComponentsCards
|
||||
);
|
||||
|
||||
const name = data.is_component ? "Component" : "Flow";
|
||||
|
|
@ -220,7 +220,7 @@ export default function CollectionCardComponent({
|
|||
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]",
|
||||
disabled ? "pointer-events-none opacity-50" : "",
|
||||
onClick ? "cursor-pointer" : "",
|
||||
isSelectedCard ? "border border-selected" : "",
|
||||
isSelectedCard ? "border border-selected" : ""
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
|
|
@ -233,7 +233,7 @@ export default function CollectionCardComponent({
|
|||
"visible flex-shrink-0",
|
||||
data.is_component
|
||||
? "mx-0.5 h-6 w-6 text-component-icon"
|
||||
: "h-7 w-7 flex-shrink-0 text-flow-icon",
|
||||
: "h-7 w-7 flex-shrink-0 text-flow-icon"
|
||||
)}
|
||||
name={data.is_component ? "ToyBrick" : "Group"}
|
||||
/>
|
||||
|
|
@ -428,7 +428,7 @@ export default function CollectionCardComponent({
|
|||
name="Trash2"
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
!authorized ? " text-ring" : "",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
|
|
@ -463,7 +463,7 @@ export default function CollectionCardComponent({
|
|||
liked_by_user
|
||||
? "fill-destructive stroke-destructive"
|
||||
: "",
|
||||
!authorized ? " text-ring" : "",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
|
|
@ -501,7 +501,7 @@ export default function CollectionCardComponent({
|
|||
}
|
||||
className={cn(
|
||||
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
|
||||
!authorized ? " text-ring" : "",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default function CardsWrapComponent({
|
|||
"h-full w-full",
|
||||
isDragging
|
||||
? "mb-36 flex flex-col items-center justify-center gap-4 text-2xl font-light"
|
||||
: "",
|
||||
: ""
|
||||
)}
|
||||
>
|
||||
{isDragging ? (
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default function FlowToolbar(): JSX.Element {
|
|||
"relative inline-flex h-full w-full items-center justify-center gap-[4px] bg-muted px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-background hover:bg-hover ",
|
||||
!hasApiKey || !validApiKey || !hasStore
|
||||
? " button-disable text-muted-foreground "
|
||||
: "",
|
||||
: ""
|
||||
)}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
|
|
@ -59,14 +59,14 @@ export default function FlowToolbar(): JSX.Element {
|
|||
"-m-0.5 -ml-1 h-6 w-6",
|
||||
!hasApiKey || !validApiKey || !hasStore
|
||||
? "extra-side-bar-save-disable"
|
||||
: "",
|
||||
: ""
|
||||
)}
|
||||
/>
|
||||
Share
|
||||
</button>
|
||||
</ShareModal>
|
||||
),
|
||||
[hasApiKey, validApiKey, currentFlow, hasStore],
|
||||
[hasApiKey, validApiKey, currentFlow, hasStore]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
@ -118,7 +118,7 @@ export default function FlowToolbar(): JSX.Element {
|
|||
<ApiModal flow={currentFlow}>
|
||||
<div
|
||||
className={classNames(
|
||||
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover",
|
||||
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover"
|
||||
)}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ export default function CodeTabsComponent({
|
|||
<div className="api-modal-according-display">
|
||||
<div
|
||||
className={classNames(
|
||||
"h-[70vh] w-full overflow-y-auto overflow-x-hidden rounded-lg bg-muted custom-scroll",
|
||||
"h-[70vh] w-full overflow-y-auto overflow-x-hidden rounded-lg bg-muted custom-scroll"
|
||||
)}
|
||||
>
|
||||
{data?.map((node: any, i) => (
|
||||
|
|
@ -275,8 +275,8 @@ export default function CodeTabsComponent({
|
|||
.show &&
|
||||
LANGFLOW_SUPPORTED_TYPES.has(
|
||||
node.data.node.template[templateField]
|
||||
.type,
|
||||
),
|
||||
.type
|
||||
)
|
||||
)
|
||||
.map((templateField, indx) => {
|
||||
return (
|
||||
|
|
@ -334,7 +334,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -380,7 +380,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -433,7 +433,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -470,7 +470,7 @@ export default function CodeTabsComponent({
|
|||
e,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
size="small"
|
||||
|
|
@ -501,7 +501,7 @@ export default function CodeTabsComponent({
|
|||
].fileTypes
|
||||
}
|
||||
onFileChange={(
|
||||
value: any,
|
||||
value: any
|
||||
) => {
|
||||
node.data.node.template[
|
||||
templateField
|
||||
|
|
@ -554,7 +554,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -594,7 +594,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
value={
|
||||
|
|
@ -656,7 +656,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -702,7 +702,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -748,7 +748,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -780,8 +780,8 @@ export default function CodeTabsComponent({
|
|||
].value,
|
||||
type(
|
||||
node,
|
||||
templateField,
|
||||
),
|
||||
templateField
|
||||
)
|
||||
)
|
||||
}
|
||||
duplicateKey={
|
||||
|
|
@ -790,15 +790,15 @@ export default function CodeTabsComponent({
|
|||
onChange={(target) => {
|
||||
const valueToNumbers =
|
||||
convertValuesToNumbers(
|
||||
target,
|
||||
target
|
||||
);
|
||||
node.data.node!.template[
|
||||
templateField
|
||||
].value = valueToNumbers;
|
||||
setErrorDuplicateKey(
|
||||
hasDuplicateKeys(
|
||||
valueToNumbers,
|
||||
),
|
||||
valueToNumbers
|
||||
)
|
||||
);
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
|
|
@ -815,7 +815,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
isList={
|
||||
|
|
@ -863,7 +863,7 @@ export default function CodeTabsComponent({
|
|||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
],
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ function CsvOutputComponent({
|
|||
if (file) {
|
||||
const { rowData: data, colDefs: columns } = convertCSVToData(
|
||||
file,
|
||||
separator,
|
||||
separator
|
||||
);
|
||||
setRowData(data);
|
||||
setColDefs(columns);
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ export default function Dropdown({
|
|||
|
||||
const refButton = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const PopoverContentDropdown = children
|
||||
? PopoverContent
|
||||
: PopoverContentWithoutPortal;
|
||||
const PopoverContentDropdown =
|
||||
children || editNode ? PopoverContent : PopoverContentWithoutPortal;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
{setEndpointName && (
|
||||
<Label>
|
||||
<div className="edit-flow-arrangement mt-3">
|
||||
<span className="font-medium">Endpoint name:</span>
|
||||
<span className="font-medium">Endpoint Name</span>
|
||||
{!isEndpointNameValid && (
|
||||
<span className="edit-flow-span">
|
||||
Invalid endpoint name. Use only letters, numbers, hyphens, and
|
||||
|
|
@ -123,7 +123,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
type="text"
|
||||
name="endpoint_name"
|
||||
value={endpointName ?? ""}
|
||||
placeholder="An alternative name for the run endpoint"
|
||||
placeholder="An alternative name to run the endpoint"
|
||||
maxLength={maxLength}
|
||||
id="endpoint_name"
|
||||
onDoubleClickCapture={(event) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import BaseModal from "../../modals/baseModal";
|
||||
import { fetchErrorComponentType } from "../../types/components";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
export default function FetchErrorComponent({
|
||||
message,
|
||||
|
|
@ -12,7 +11,14 @@ export default function FetchErrorComponent({
|
|||
}: fetchErrorComponentType) {
|
||||
return (
|
||||
<>
|
||||
<BaseModal size="small-h-full" open={openModal} type="modal">
|
||||
<BaseModal
|
||||
size="small-h-full"
|
||||
open={openModal}
|
||||
type="modal"
|
||||
onSubmit={() => {
|
||||
setRetry();
|
||||
}}
|
||||
>
|
||||
<BaseModal.Content>
|
||||
<div role="status" className="m-auto flex flex-col items-center">
|
||||
<IconComponent
|
||||
|
|
@ -27,24 +33,9 @@ export default function FetchErrorComponent({
|
|||
</div>
|
||||
</BaseModal.Content>
|
||||
|
||||
<BaseModal.Footer>
|
||||
<div className="m-auto">
|
||||
<Button
|
||||
disabled={isLoadingHealth}
|
||||
onClick={() => {
|
||||
setRetry();
|
||||
}}
|
||||
>
|
||||
{isLoadingHealth ? (
|
||||
<div>
|
||||
<IconComponent name={"Loader2"} className={"animate-spin"} />
|
||||
</div>
|
||||
) : (
|
||||
"Retry"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</BaseModal.Footer>
|
||||
<BaseModal.Footer
|
||||
submit={{ label: "Retry", loading: isLoadingHealth }}
|
||||
/>
|
||||
</BaseModal>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -37,21 +37,11 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||
const getTypes = useTypesStore((state) => state.getTypes);
|
||||
|
||||
function handleAddFlow(duplicate?: boolean) {
|
||||
function handleAddFlow() {
|
||||
try {
|
||||
if (duplicate) {
|
||||
if (!currentFlow) {
|
||||
throw new Error("No flow to duplicate");
|
||||
}
|
||||
addFlow(true, currentFlow).then((id) => {
|
||||
setSuccessData({ title: "Flow duplicated successfully" });
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
} else {
|
||||
addFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}
|
||||
addFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
} catch (err) {
|
||||
setErrorData(err as { title: string; list?: Array<string> });
|
||||
}
|
||||
|
|
@ -97,15 +87,6 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
<IconComponent name="Plus" className="header-menu-options" />
|
||||
New
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
handleAddFlow(true);
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<IconComponent name="Copy" className="header-menu-options" />
|
||||
Duplicate
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default function Header(): JSX.Element {
|
|||
const lastFlowVisitedIndex = routeHistory
|
||||
.reverse()
|
||||
.findIndex(
|
||||
(path) => path.includes("/flow/") && path !== location.pathname
|
||||
(path) => path.includes("/flow/") && path !== location.pathname,
|
||||
);
|
||||
|
||||
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
|
||||
|
|
@ -81,14 +81,16 @@ export default function Header(): JSX.Element {
|
|||
<span className="ml-4 text-2xl">⛓️</span>
|
||||
</Link>
|
||||
{showArrowReturnIcon && (
|
||||
<button
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
onClick={() => {
|
||||
checkForChanges();
|
||||
redirectToLastLocation();
|
||||
}}
|
||||
>
|
||||
<IconComponent name="ChevronLeft" className="w-4" />
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<MenuBar />
|
||||
|
|
@ -181,24 +183,14 @@ export default function Header(): JSX.Element {
|
|||
/>
|
||||
</div>
|
||||
</AlertDropdown>
|
||||
{autoLogin && (
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/account/api-keys");
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Key"
|
||||
className="side-bar-button-size text-muted-foreground hover:text-accent-foreground"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<>
|
||||
<Separator orientation="vertical" />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
data-testid="user-profile-settings"
|
||||
className={
|
||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||
|
|
@ -212,6 +204,28 @@ export default function Header(): JSX.Element {
|
|||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{!autoLogin && (
|
||||
<>
|
||||
<DropdownMenuLabel>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={
|
||||
"h-5 w-5 rounded-full focus-visible:outline-0 " +
|
||||
(userData?.profile_image ??
|
||||
(userData?.id
|
||||
? gradients[
|
||||
parseInt(userData?.id ?? "", 30) %
|
||||
gradients.length
|
||||
]
|
||||
: "bg-gray-500"))
|
||||
}
|
||||
/>
|
||||
{userData?.username ?? "User"}
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<DropdownMenuLabel>General</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,11 @@ import {
|
|||
CommandList,
|
||||
} from "../../../ui/command";
|
||||
import { Input } from "../../../ui/input";
|
||||
import { Popover, PopoverContentWithoutPortal } from "../../../ui/popover";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverContentWithoutPortal,
|
||||
} from "../../../ui/popover";
|
||||
const CustomInputPopover = ({
|
||||
id,
|
||||
refInput,
|
||||
|
|
@ -39,6 +43,9 @@ const CustomInputPopover = ({
|
|||
showOptions,
|
||||
}) => {
|
||||
const setErrorData = useAlertStore.getState().setErrorData;
|
||||
const PopoverContentInput = editNode
|
||||
? PopoverContent
|
||||
: PopoverContentWithoutPortal;
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
if (password) {
|
||||
|
|
@ -68,9 +75,9 @@ const CustomInputPopover = ({
|
|||
(selectedOption !== "" || !onChange) && setSelectedOption
|
||||
? selectedOption
|
||||
: (selectedOptions?.length !== 0 || !onChange) &&
|
||||
setSelectedOptions
|
||||
? selectedOptions?.join(", ")
|
||||
: value
|
||||
setSelectedOptions
|
||||
? selectedOptions?.join(", ")
|
||||
: value
|
||||
}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
|
|
@ -96,7 +103,7 @@ const CustomInputPopover = ({
|
|||
(password && !(setSelectedOption || setSelectedOptions))
|
||||
? "pr-8"
|
||||
: "",
|
||||
className!,
|
||||
className!
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={handleInputChange}
|
||||
|
|
@ -107,8 +114,8 @@ const CustomInputPopover = ({
|
|||
data-testid={editNode ? id + "-edit" : id}
|
||||
/>
|
||||
</PopoverAnchor>
|
||||
<PopoverContentWithoutPortal
|
||||
className="nocopy nopan nodelete nodrag noundo p-0"
|
||||
<PopoverContentInput
|
||||
className="nocopy nowheel nopan nodelete nodrag noundo p-0"
|
||||
style={{ minWidth: refInput?.current?.clientWidth ?? "200px" }}
|
||||
side="bottom"
|
||||
align="center"
|
||||
|
|
@ -134,15 +141,15 @@ const CustomInputPopover = ({
|
|||
onSelect={(currentValue) => {
|
||||
setSelectedOption &&
|
||||
setSelectedOption(
|
||||
currentValue === selectedOption ? "" : currentValue,
|
||||
currentValue === selectedOption ? "" : currentValue
|
||||
);
|
||||
setSelectedOptions &&
|
||||
setSelectedOptions(
|
||||
selectedOptions?.includes(currentValue)
|
||||
? selectedOptions.filter(
|
||||
(item) => item !== currentValue,
|
||||
(item) => item !== currentValue
|
||||
)
|
||||
: [...selectedOptions, currentValue],
|
||||
: [...selectedOptions, currentValue]
|
||||
);
|
||||
!setSelectedOptions && setShowOptions(false);
|
||||
}}
|
||||
|
|
@ -155,7 +162,7 @@ const CustomInputPopover = ({
|
|||
selectedOption === option ||
|
||||
selectedOptions?.includes(option)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
: "opacity-0"
|
||||
)}
|
||||
>
|
||||
<div className="absolute opacity-100 transition-all group-hover:opacity-0">
|
||||
|
|
@ -184,7 +191,7 @@ const CustomInputPopover = ({
|
|||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContentWithoutPortal>
|
||||
</PopoverContentInput>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ import {
|
|||
CommandList,
|
||||
} from "../../../ui/command";
|
||||
import { Input } from "../../../ui/input";
|
||||
import { Popover, PopoverContentWithoutPortal } from "../../../ui/popover";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverContentWithoutPortal,
|
||||
} from "../../../ui/popover";
|
||||
const CustomInputPopoverObject = ({
|
||||
id,
|
||||
refInput,
|
||||
|
|
@ -23,6 +27,7 @@ const CustomInputPopoverObject = ({
|
|||
disabled,
|
||||
setShowOptions,
|
||||
required,
|
||||
editNode,
|
||||
className,
|
||||
placeholder,
|
||||
onChange,
|
||||
|
|
@ -34,6 +39,10 @@ const CustomInputPopoverObject = ({
|
|||
handleKeyDown,
|
||||
showOptions,
|
||||
}) => {
|
||||
const PopoverContentInput = editNode
|
||||
? PopoverContent
|
||||
: PopoverContentWithoutPortal;
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
onChange && onChange(e.target.value);
|
||||
};
|
||||
|
|
@ -51,14 +60,14 @@ const CustomInputPopoverObject = ({
|
|||
? options.find((option) => option.id === selectedOption)?.name ||
|
||||
""
|
||||
: (selectedOptions?.length !== 0 || !onChange) &&
|
||||
setSelectedOptions
|
||||
? selectedOptions
|
||||
.map(
|
||||
(optionId) =>
|
||||
options.find((option) => option.id === optionId)?.name,
|
||||
)
|
||||
.join(", ")
|
||||
: value
|
||||
setSelectedOptions
|
||||
? selectedOptions
|
||||
.map(
|
||||
(optionId) =>
|
||||
options.find((option) => option.id === optionId)?.name
|
||||
)
|
||||
.join(", ")
|
||||
: value
|
||||
}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
|
|
@ -79,8 +88,8 @@ const CustomInputPopoverObject = ({
|
|||
data-testid={id}
|
||||
/>
|
||||
</PopoverAnchor>
|
||||
<PopoverContentWithoutPortal
|
||||
className="nocopy nopan nodelete nodrag noundo p-0"
|
||||
<PopoverContentInput
|
||||
className="nocopy nowheel nopan nodelete nodrag noundo p-0"
|
||||
style={{ minWidth: refInput?.current?.clientWidth ?? "200px" }}
|
||||
side="bottom"
|
||||
align="center"
|
||||
|
|
@ -106,15 +115,15 @@ const CustomInputPopoverObject = ({
|
|||
onSelect={(currentValue) => {
|
||||
setSelectedOption &&
|
||||
setSelectedOption(
|
||||
currentValue === selectedOption ? "" : currentValue,
|
||||
currentValue === selectedOption ? "" : currentValue
|
||||
);
|
||||
setSelectedOptions &&
|
||||
setSelectedOptions(
|
||||
selectedOptions?.includes(currentValue)
|
||||
? selectedOptions.filter(
|
||||
(item) => item !== currentValue,
|
||||
(item) => item !== currentValue
|
||||
)
|
||||
: [...selectedOptions, currentValue],
|
||||
: [...selectedOptions, currentValue]
|
||||
);
|
||||
!setSelectedOptions && setShowOptions(false);
|
||||
}}
|
||||
|
|
@ -127,7 +136,7 @@ const CustomInputPopoverObject = ({
|
|||
selectedOption === option.id ||
|
||||
selectedOptions?.includes(option.id)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
: "opacity-0"
|
||||
)}
|
||||
>
|
||||
<div className="absolute opacity-100 transition-all group-hover:opacity-0">
|
||||
|
|
@ -159,7 +168,7 @@ const CustomInputPopoverObject = ({
|
|||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContentWithoutPortal>
|
||||
</PopoverContentInput>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export default function InputComponent({
|
|||
editNode ? " input-edit-node " : "",
|
||||
password && editNode ? "pr-8" : "",
|
||||
password && !editNode ? "pr-10" : "",
|
||||
className!,
|
||||
className!
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={(e) => {
|
||||
|
|
@ -108,6 +108,7 @@ export default function InputComponent({
|
|||
setSelectedOptions={setSelectedOptions}
|
||||
options={objectOptions}
|
||||
value={value}
|
||||
editNode={editNode}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
setShowOptions={setShowOptions}
|
||||
|
|
@ -153,7 +154,7 @@ export default function InputComponent({
|
|||
<span
|
||||
className={cn(
|
||||
password && selectedOption === "" ? "right-8" : "right-0",
|
||||
"absolute inset-y-0 flex items-center pr-2.5",
|
||||
"absolute inset-y-0 flex items-center pr-2.5"
|
||||
)}
|
||||
>
|
||||
<button
|
||||
|
|
@ -166,7 +167,7 @@ export default function InputComponent({
|
|||
selectedOption !== ""
|
||||
? "text-medium-indigo"
|
||||
: "text-muted-foreground",
|
||||
"hover:text-accent-foreground",
|
||||
"hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
|
|
@ -186,7 +187,7 @@ export default function InputComponent({
|
|||
"mb-px",
|
||||
editNode
|
||||
? "input-component-true-button"
|
||||
: "input-component-false-button",
|
||||
: "input-component-false-button"
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
|
@ -203,7 +204,7 @@ export default function InputComponent({
|
|||
className={classNames(
|
||||
editNode
|
||||
? "input-component-true-svg"
|
||||
: "input-component-false-svg",
|
||||
: "input-component-false-svg"
|
||||
)}
|
||||
>
|
||||
<path
|
||||
|
|
@ -222,7 +223,7 @@ export default function InputComponent({
|
|||
className={classNames(
|
||||
editNode
|
||||
? "input-component-true-svg"
|
||||
: "input-component-false-svg",
|
||||
: "input-component-false-svg"
|
||||
)}
|
||||
>
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
CONSOLE_ERROR_MSG,
|
||||
CONSOLE_SUCCESS_MSG,
|
||||
INVALID_FILE_ALERT,
|
||||
} from "../../constants/alerts_constants";
|
||||
import { uploadFile } from "../../controllers/API";
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ export default function InputGlobalComponent({
|
|||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
useEffect(() => {
|
||||
if (data.node?.template[name])
|
||||
if (data)
|
||||
if (
|
||||
globalVariablesEntries &&
|
||||
!globalVariablesEntries.includes(data.node?.template[name].value) &&
|
||||
data.node?.template[name].load_from_db
|
||||
!globalVariablesEntries.includes(data.value) &&
|
||||
data.load_from_db
|
||||
) {
|
||||
setTimeout(() => {
|
||||
onChange("", true);
|
||||
|
|
@ -46,17 +46,11 @@ export default function InputGlobalComponent({
|
|||
}, [globalVariablesEntries]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!data.node?.template[name].value &&
|
||||
data.node?.template[name].display_name
|
||||
) {
|
||||
if (
|
||||
unavaliableFields[data.node?.template[name].display_name!] &&
|
||||
!disabled
|
||||
) {
|
||||
if (!data.value && data.display_name) {
|
||||
if (unavaliableFields[data.display_name!] && !disabled) {
|
||||
setTimeout(() => {
|
||||
setDb(true);
|
||||
onChange(unavaliableFields[data.node?.template[name].display_name!]);
|
||||
onChange(unavaliableFields[data.display_name!]);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
|
@ -68,10 +62,7 @@ export default function InputGlobalComponent({
|
|||
await deleteGlobalVariable(id)
|
||||
.then(() => {
|
||||
removeGlobalVariable(key);
|
||||
if (
|
||||
data?.node?.template[name].value === key &&
|
||||
data?.node?.template[name].load_from_db
|
||||
) {
|
||||
if (data?.value === key && data?.load_from_db) {
|
||||
onChange("");
|
||||
setDb(false);
|
||||
}
|
||||
|
|
@ -94,8 +85,8 @@ export default function InputGlobalComponent({
|
|||
id={"input-" + name}
|
||||
editNode={editNode}
|
||||
disabled={disabled}
|
||||
password={data.node?.template[name].password ?? false}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
password={data.password ?? false}
|
||||
value={data.value ?? ""}
|
||||
options={globalVariablesEntries}
|
||||
optionsPlaceholder={"Global Variables"}
|
||||
optionsIcon="Globe"
|
||||
|
|
@ -138,10 +129,10 @@ export default function InputGlobalComponent({
|
|||
</DeleteConfirmationModal>
|
||||
)}
|
||||
selectedOption={
|
||||
data?.node?.template[name].load_from_db &&
|
||||
data?.load_from_db &&
|
||||
globalVariablesEntries &&
|
||||
globalVariablesEntries.includes(data?.node?.template[name].value ?? "")
|
||||
? data?.node?.template[name].value
|
||||
globalVariablesEntries.includes(data?.value ?? "")
|
||||
? data?.value
|
||||
: ""
|
||||
}
|
||||
setSelectedOption={(value) => {
|
||||
|
|
|
|||
|
|
@ -55,10 +55,11 @@ export default function InputListComponent({
|
|||
/>
|
||||
{idx === value.length - 1 ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
let newInputList = _.cloneDeep(value);
|
||||
newInputList.push("");
|
||||
onChange(newInputList);
|
||||
e.preventDefault();
|
||||
}}
|
||||
data-testid={
|
||||
`input-list-plus-btn${
|
||||
|
|
@ -79,10 +80,11 @@ export default function InputListComponent({
|
|||
editNode ? "-edit" : ""
|
||||
}_${componentName}-` + idx
|
||||
}
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
let newInputList = _.cloneDeep(value);
|
||||
newInputList.splice(idx, 1);
|
||||
onChange(newInputList);
|
||||
e.preventDefault();
|
||||
}}
|
||||
disabled={disabled || playgroundDisabled}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ export default function ShadTooltip({
|
|||
delayDuration = 500,
|
||||
}: ShadToolTipType): JSX.Element {
|
||||
return (
|
||||
<Tooltip delayDuration={delayDuration}>
|
||||
<Tooltip defaultOpen={!children} delayDuration={delayDuration}>
|
||||
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
||||
|
||||
<TooltipContent
|
||||
className={cn(styleClasses, "max-w-96")}
|
||||
side={side}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,12 @@ type SideBarButtonsComponentProps = {
|
|||
pathname: string;
|
||||
handleOpenNewFolderModal?: () => void;
|
||||
};
|
||||
const SideBarButtonsComponent = ({ items }: SideBarButtonsComponentProps) => {
|
||||
const SideBarButtonsComponent = ({
|
||||
items,
|
||||
pathname,
|
||||
}: SideBarButtonsComponentProps) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-2 overflow-auto lg:h-[70vh] lg:flex-col">
|
||||
{items.map((item) => (
|
||||
<Link to={item.href!}>
|
||||
<div
|
||||
|
|
@ -21,14 +24,20 @@ const SideBarButtonsComponent = ({ items }: SideBarButtonsComponentProps) => {
|
|||
data-testid={`sidebar-nav-${item.title}`}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"!w-[200px] cursor-pointer justify-start gap-2 border border-transparent hover:border-border hover:bg-transparent"
|
||||
pathname === item.href
|
||||
? "border border-border bg-muted hover:bg-muted"
|
||||
: "border border-transparent hover:border-border hover:bg-transparent",
|
||||
"flex w-full shrink-0 justify-start gap-4"
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
{item.icon}
|
||||
<span className="block max-w-full truncate opacity-100">
|
||||
{item.title}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default SideBarButtonsComponent;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import IconComponent, {
|
|||
import { Button, buttonVariants } from "../../../ui/button";
|
||||
import { Input } from "../../../ui/input";
|
||||
import useFileDrop from "../../hooks/use-on-file-drop";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
|
||||
type SideBarFoldersButtonsComponentProps = {
|
||||
folders: FolderType[];
|
||||
|
|
@ -33,7 +34,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
const [foldersNames, setFoldersNames] = useState({});
|
||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||
const [editFolders, setEditFolderName] = useState(
|
||||
folders.map((obj) => ({ name: obj.name, edit: false }))
|
||||
folders.map((obj) => ({ name: obj.name, edit: false })),
|
||||
);
|
||||
const uploadFolder = useFolderStore((state) => state.uploadFolder);
|
||||
const currentFolder = pathname.split("/");
|
||||
|
|
@ -51,6 +52,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
const location = useLocation();
|
||||
const folderId = location?.state?.folderId ?? myCollectionId;
|
||||
const getFolderById = useFolderStore((state) => state.getFolderById);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
const handleFolderChange = (folderId: string) => {
|
||||
getFolderById(folderId);
|
||||
|
|
@ -58,11 +60,21 @@ const SideBarFoldersButtonsComponent = ({
|
|||
|
||||
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
|
||||
folderId,
|
||||
handleFolderChange
|
||||
handleFolderChange,
|
||||
);
|
||||
|
||||
const handleUploadFlowsToFolder = () => {
|
||||
uploadFolder(folderId);
|
||||
uploadFolder(folderId)
|
||||
.then(() => {
|
||||
getFolderById(folderId);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setErrorData({
|
||||
title: `Error on upload`,
|
||||
list: [err["response"]["data"]],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownloadFolder = (id: string) => {
|
||||
|
|
@ -73,7 +85,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
|
||||
(res) => {
|
||||
getFoldersApi(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -93,24 +105,25 @@ const SideBarFoldersButtonsComponent = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="flex shrink-0 items-center justify-between">
|
||||
<Button variant="primary" onClick={addNewFolder}>
|
||||
<ForwardedIconComponent
|
||||
name="Plus"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
New Folder
|
||||
<div className="flex shrink-0 items-center justify-between gap-2">
|
||||
<div className="flex-1 self-start text-lg font-semibold">Folders</div>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="icon"
|
||||
className="px-2"
|
||||
onClick={addNewFolder}
|
||||
data-testid="add-folder-button"
|
||||
>
|
||||
<ForwardedIconComponent name="FolderPlus" className="w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="px-7"
|
||||
size="icon"
|
||||
className="px-2"
|
||||
onClick={handleUploadFlowsToFolder}
|
||||
data-testid="upload-folder-button"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="Upload"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Upload
|
||||
<ForwardedIconComponent name="Upload" className="w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -118,7 +131,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
<>
|
||||
{folders.map((item, index) => {
|
||||
const editFolderName = editFolders?.filter(
|
||||
(folder) => folder.name === item.name
|
||||
(folder) => folder.name === item.name,
|
||||
)[0];
|
||||
return (
|
||||
<div
|
||||
|
|
@ -134,7 +147,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
? "border border-border bg-muted hover:bg-muted"
|
||||
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
|
||||
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
|
||||
folderIdDragging === item.id! ? "bg-border" : ""
|
||||
folderIdDragging === item.id! ? "bg-border" : "",
|
||||
)}
|
||||
onClick={() => handleChangeFolder!(item.id!)}
|
||||
>
|
||||
|
|
@ -176,11 +189,11 @@ const SideBarFoldersButtonsComponent = ({
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}}
|
||||
className="flex w-full items-center gap-2"
|
||||
className="flex w-full items-center gap-4"
|
||||
>
|
||||
<IconComponent
|
||||
name={"folder"}
|
||||
className="mr-2 w-4 flex-shrink-0 justify-start stroke-[1.5] opacity-100"
|
||||
className="w-4 flex-shrink-0 justify-start stroke-[1.5] opacity-100"
|
||||
/>
|
||||
{editFolderName?.edit ? (
|
||||
<div>
|
||||
|
|
@ -204,7 +217,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
folders.map((obj) => ({
|
||||
name: obj.name,
|
||||
edit: false,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
}
|
||||
if (e.key === "Enter") {
|
||||
|
|
@ -237,10 +250,10 @@ const SideBarFoldersButtonsComponent = ({
|
|||
};
|
||||
const updatedFolder = await updateFolder(
|
||||
body,
|
||||
item.id!
|
||||
item.id!,
|
||||
);
|
||||
const updateFolders = folders.filter(
|
||||
(f) => f.name !== item.name
|
||||
(f) => f.name !== item.name,
|
||||
);
|
||||
setFolders([...updateFolders, updatedFolder]);
|
||||
setFoldersNames({});
|
||||
|
|
@ -248,7 +261,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
folders.map((obj) => ({
|
||||
name: obj.name,
|
||||
edit: false,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
} else {
|
||||
setFoldersNames((old) => ({
|
||||
|
|
@ -259,14 +272,14 @@ const SideBarFoldersButtonsComponent = ({
|
|||
}}
|
||||
value={foldersNames[item.name]}
|
||||
id={`input-folder-${item.name}`}
|
||||
data-testid={`input-folder`}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<span className="block max-w-full truncate opacity-100">
|
||||
<span className="block w-full truncate opacity-100">
|
||||
{item.name}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex-1" />
|
||||
{index > 0 && (
|
||||
<Button
|
||||
className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]"
|
||||
|
|
@ -283,21 +296,6 @@ const SideBarFoldersButtonsComponent = ({
|
|||
/>
|
||||
</Button>
|
||||
)}
|
||||
{/* {index > 0 && (
|
||||
<Button
|
||||
className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
variant={"ghost"}
|
||||
>
|
||||
<IconComponent
|
||||
name={"pencil"}
|
||||
className=" w-4 stroke-[1.5] text-white "
|
||||
/>
|
||||
</Button>
|
||||
)} */}
|
||||
<Button
|
||||
className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]"
|
||||
onClick={(e) => {
|
||||
|
|
@ -305,7 +303,8 @@ const SideBarFoldersButtonsComponent = ({
|
|||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
variant={"ghost"}
|
||||
size="none"
|
||||
variant="none"
|
||||
>
|
||||
<IconComponent
|
||||
name={"Download"}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { addVersionToDuplicates } from "../../../utils/reactflowUtils";
|
|||
const useFileDrop = (folderId, folderChangeCallback) => {
|
||||
const setFolderDragging = useFolderStore((state) => state.setFolderDragging);
|
||||
const setFolderIdDragging = useFolderStore(
|
||||
(state) => state.setFolderIdDragging,
|
||||
(state) => state.setFolderIdDragging
|
||||
);
|
||||
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
|
@ -45,7 +45,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
| React.DragEvent<HTMLDivElement>
|
||||
| React.DragEvent<HTMLButtonElement>
|
||||
| React.DragEvent<HTMLAnchorElement>,
|
||||
folderId: string,
|
||||
folderId: string
|
||||
) => {
|
||||
e.preventDefault();
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
| React.DragEvent<HTMLDivElement>
|
||||
| React.DragEvent<HTMLButtonElement>
|
||||
| React.DragEvent<HTMLAnchorElement>,
|
||||
folderId: string,
|
||||
folderId: string
|
||||
) => {
|
||||
if (e.dataTransfer.types.some((types) => types === "Files")) {
|
||||
setFolderDragging(true);
|
||||
|
|
@ -73,7 +73,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
e:
|
||||
| React.DragEvent<HTMLDivElement>
|
||||
| React.DragEvent<HTMLButtonElement>
|
||||
| React.DragEvent<HTMLAnchorElement>,
|
||||
| React.DragEvent<HTMLAnchorElement>
|
||||
) => {
|
||||
e.preventDefault();
|
||||
if (e.target === e.currentTarget) {
|
||||
|
|
@ -87,7 +87,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
| React.DragEvent<HTMLDivElement>
|
||||
| React.DragEvent<HTMLButtonElement>
|
||||
| React.DragEvent<HTMLAnchorElement>,
|
||||
folderId: string,
|
||||
folderId: string
|
||||
) => {
|
||||
if (e?.dataTransfer?.getData("flow")) {
|
||||
const data = JSON.parse(e?.dataTransfer?.getData("flow"));
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@ import { cn } from "../../utils/utils";
|
|||
import HorizontalScrollFadeComponent from "../horizontalScrollFadeComponent";
|
||||
import SideBarButtonsComponent from "./components/sideBarButtons";
|
||||
import SideBarFoldersButtonsComponent from "./components/sideBarFolderButtons";
|
||||
import { addFolder } from "../../pages/MainPage/services";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
|
||||
type SidebarNavProps = {
|
||||
items: {
|
||||
|
|
@ -15,7 +12,6 @@ type SidebarNavProps = {
|
|||
title: string;
|
||||
icon: React.ReactNode;
|
||||
}[];
|
||||
handleOpenNewFolderModal?: () => void;
|
||||
handleChangeFolder?: (id: string) => void;
|
||||
handleEditFolder?: (item: FolderType) => void;
|
||||
handleDeleteFolder?: (item: FolderType) => void;
|
||||
|
|
@ -41,16 +37,20 @@ export default function SidebarNav({
|
|||
return (
|
||||
<nav className={cn(className)} {...props}>
|
||||
<HorizontalScrollFadeComponent>
|
||||
<SideBarButtonsComponent items={items} pathname={pathname} />
|
||||
|
||||
{!loadingFolders && folders?.length > 0 && isFolderPath && (
|
||||
<SideBarFoldersButtonsComponent
|
||||
folders={folders}
|
||||
pathname={pathname}
|
||||
handleChangeFolder={handleChangeFolder}
|
||||
handleEditFolder={handleEditFolder}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
/>
|
||||
{items.length > 0 ? (
|
||||
<SideBarButtonsComponent items={items} pathname={pathname} />
|
||||
) : (
|
||||
!loadingFolders &&
|
||||
folders?.length > 0 &&
|
||||
isFolderPath && (
|
||||
<SideBarFoldersButtonsComponent
|
||||
folders={folders}
|
||||
pathname={pathname}
|
||||
handleChangeFolder={handleChangeFolder}
|
||||
handleEditFolder={handleEditFolder}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</HorizontalScrollFadeComponent>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import { cn } from "../../../../utils/utils";
|
||||
|
||||
export default function ResetColumns({
|
||||
resetGrid,
|
||||
}: {
|
||||
resetGrid: () => void;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
/*<div className="absolute left-2 bottom-1 cursor-pointer">
|
||||
<div
|
||||
className="flex h-10 items-center justify-center px-2 pl-3 rounded-md border border-ring/60 text-sm text-[#bccadc] ring-offset-background placeholder:text-muted-foreground hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={() => setShow(!show)}
|
||||
>
|
||||
<ForwardedIconComponent name="Settings"></ForwardedIconComponent>
|
||||
<ForwardedIconComponent name={show ? "ChevronLeft" : "ChevronRight"} className="transition-all"></ForwardedIconComponent>
|
||||
</div>
|
||||
</div>*/
|
||||
<div className={cn("absolute bottom-4 left-6")}>
|
||||
<span
|
||||
className="cursor-pointer underline"
|
||||
onClick={() => {
|
||||
resetGrid();
|
||||
}}
|
||||
>
|
||||
Reset Columns
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { CustomCellRendererProps } from "ag-grid-react";
|
||||
import { cn, isTimeStampString } from "../../utils/utils";
|
||||
import ArrayReader from "../arrayReaderComponent";
|
||||
import DateReader from "../dateReaderComponent";
|
||||
import NumberReader from "../numberReader";
|
||||
import ObjectRender from "../objectRender";
|
||||
import StringReader from "../stringReaderComponent";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { cn, isTimeStampString } from "../../../../utils/utils";
|
||||
import ArrayReader from "../../../arrayReaderComponent";
|
||||
import DateReader from "../../../dateReaderComponent";
|
||||
import NumberReader from "../../../numberReader";
|
||||
import ObjectRender from "../../../objectRender";
|
||||
import StringReader from "../../../stringReaderComponent";
|
||||
import { Badge } from "../../../ui/badge";
|
||||
|
||||
export default function TableAutoCellRender({
|
||||
value,
|
||||
|
|
@ -43,7 +43,6 @@ export default function TableAutoCellRender({
|
|||
} else {
|
||||
return <StringReader string={value} />;
|
||||
}
|
||||
break;
|
||||
case "number":
|
||||
return <NumberReader number={value} />;
|
||||
default:
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
import { CustomCellRendererProps } from "ag-grid-react";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { useState } from "react";
|
||||
import CodeAreaComponent from "../../../codeAreaComponent";
|
||||
import DictComponent from "../../../dictComponent";
|
||||
import Dropdown from "../../../dropdownComponent";
|
||||
import FloatComponent from "../../../floatComponent";
|
||||
import InputFileComponent from "../../../inputFileComponent";
|
||||
import InputGlobalComponent from "../../../inputGlobalComponent";
|
||||
import InputListComponent from "../../../inputListComponent";
|
||||
import IntComponent from "../../../intComponent";
|
||||
import KeypairListComponent from "../../../keypairListComponent";
|
||||
import PromptAreaComponent from "../../../promptComponent";
|
||||
import TextAreaComponent from "../../../textAreaComponent";
|
||||
import ToggleShadComponent from "../../../toggleShadComponent";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import {
|
||||
convertObjToArray,
|
||||
convertValuesToNumbers,
|
||||
hasDuplicateKeys,
|
||||
scapedJSONStringfy,
|
||||
} from "../../../../utils/reactflowUtils";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
|
||||
export default function TableNodeCellRender({
|
||||
node: { data },
|
||||
value: {
|
||||
value,
|
||||
nodeClass,
|
||||
handleOnNewValue: handleOnNewValueNode,
|
||||
handleOnChangeDb,
|
||||
},
|
||||
}: CustomCellRendererProps) {
|
||||
const handleOnNewValue = (newValue: any, name: string) => {
|
||||
handleOnNewValueNode(newValue, name);
|
||||
setTemplateData((old) => {
|
||||
let newData = cloneDeep(old);
|
||||
newData.value = newValue;
|
||||
return newData;
|
||||
});
|
||||
setTemplateValue(newValue);
|
||||
};
|
||||
|
||||
const [templateValue, setTemplateValue] = useState(value);
|
||||
const [templateData, setTemplateData] = useState(data);
|
||||
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
|
||||
const id = {
|
||||
inputTypes: templateData.input_types,
|
||||
type: templateData.type,
|
||||
id: nodeClass.id,
|
||||
fieldName: templateData.key,
|
||||
};
|
||||
const disabled =
|
||||
edges.some(
|
||||
(edge) =>
|
||||
edge.targetHandle ===
|
||||
scapedJSONStringfy(
|
||||
templateData.proxy
|
||||
? {
|
||||
...id,
|
||||
proxy: templateData.proxy,
|
||||
}
|
||||
: id,
|
||||
),
|
||||
) ?? false;
|
||||
function getCellType() {
|
||||
switch (templateData.type) {
|
||||
case "str":
|
||||
if (!templateData.options) {
|
||||
return templateData?.list ? (
|
||||
<InputListComponent
|
||||
componentName={templateData.key ?? undefined}
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
value={
|
||||
!templateValue || templateValue === "" ? [""] : templateValue
|
||||
}
|
||||
onChange={(value: string[]) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
/>
|
||||
) : templateData.multiline ? (
|
||||
<TextAreaComponent
|
||||
id={"textarea-edit-" + templateData.name}
|
||||
data-testid={"textarea-edit-" + templateData.name}
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={templateValue ?? ""}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputGlobalComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
onChange={(value) => handleOnNewValue(value, templateData.key)}
|
||||
setDb={(value) => {
|
||||
handleOnChangeDb(value, templateData.key);
|
||||
}}
|
||||
name={templateData.key}
|
||||
data={templateData}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Dropdown
|
||||
editNode={true}
|
||||
options={templateData.options}
|
||||
onSelect={(value) => handleOnNewValue(value, templateData.key)}
|
||||
value={templateValue ?? "Choose an option"}
|
||||
id={"dropdown-edit-" + templateData.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case "NestedDict":
|
||||
return (
|
||||
<DictComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={templateValue.toString() === "{}" ? {} : templateValue}
|
||||
onChange={(newValue) => {
|
||||
handleOnNewValue(newValue, templateData.key);
|
||||
}}
|
||||
id="editnode-div-dict-input"
|
||||
/>
|
||||
);
|
||||
|
||||
case "dict":
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"max-h-48 w-full overflow-auto custom-scroll",
|
||||
templateValue?.length > 1 ? "my-3" : "",
|
||||
)}
|
||||
>
|
||||
<KeypairListComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
templateValue?.length === 0 || !templateValue
|
||||
? [{ "": "" }]
|
||||
: convertObjToArray(templateValue, templateData.type)
|
||||
}
|
||||
duplicateKey={errorDuplicateKey}
|
||||
onChange={(newValue) => {
|
||||
const valueToNumbers = convertValuesToNumbers(newValue);
|
||||
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
|
||||
handleOnNewValue(valueToNumbers, templateData.key);
|
||||
}}
|
||||
isList={templateData.list ?? false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case "bool":
|
||||
return (
|
||||
<ToggleShadComponent
|
||||
id={"toggle-edit-" + templateData.name}
|
||||
disabled={disabled}
|
||||
enabled={templateValue}
|
||||
setEnabled={(isEnabled) => {
|
||||
handleOnNewValue(isEnabled, templateData.key);
|
||||
}}
|
||||
size="small"
|
||||
editNode={true}
|
||||
/>
|
||||
);
|
||||
|
||||
case "float":
|
||||
return (
|
||||
<FloatComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
rangeSpec={templateData.rangeSpec}
|
||||
value={templateValue ?? ""}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case "int":
|
||||
return (
|
||||
<IntComponent
|
||||
rangeSpec={templateData.rangeSpec}
|
||||
id={"edit-int-input-" + templateData.name}
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={templateValue ?? ""}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
case "file":
|
||||
return (
|
||||
<InputFileComponent
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
value={templateValue ?? ""}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
fileTypes={templateData.fileTypes}
|
||||
onFileChange={(filePath: string) => {
|
||||
templateData.file_path = filePath;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
case "prompt":
|
||||
return (
|
||||
<PromptAreaComponent
|
||||
readonly={nodeClass.flow ? true : false}
|
||||
field_name={templateData.key}
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
nodeClass={nodeClass}
|
||||
setNodeClass={(value) => {
|
||||
nodeClass = value;
|
||||
}}
|
||||
value={templateValue ?? ""}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
id={"prompt-area-edit-" + templateData.name}
|
||||
data-testid={"modal-prompt-input-" + templateData.name}
|
||||
/>
|
||||
);
|
||||
|
||||
case "code":
|
||||
return (
|
||||
<CodeAreaComponent
|
||||
readonly={nodeClass.flow && templateData.dynamic ? true : false}
|
||||
dynamic={templateData.dynamic ?? false}
|
||||
setNodeClass={(value) => {
|
||||
nodeClass = value;
|
||||
}}
|
||||
nodeClass={nodeClass}
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={templateValue ?? ""}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateData.key);
|
||||
}}
|
||||
id={"code-area-edit" + templateData.name}
|
||||
/>
|
||||
);
|
||||
case "Any":
|
||||
return <>-</>;
|
||||
default:
|
||||
return String(templateValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="group flex h-full w-[300px] items-center justify-center py-2.5">
|
||||
{getCellType()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { CustomCellRendererProps } from "ag-grid-react";
|
||||
import { useState } from "react";
|
||||
import ToggleShadComponent from "../../../toggleShadComponent";
|
||||
|
||||
export default function TableToggleCellRender({
|
||||
value: { name, enabled, setEnabled },
|
||||
}: CustomCellRendererProps) {
|
||||
const [value, setValue] = useState(enabled);
|
||||
|
||||
return (
|
||||
<div className="flex h-full items-center">
|
||||
<ToggleShadComponent
|
||||
id={"show" + name}
|
||||
enabled={value}
|
||||
setEnabled={(e) => {
|
||||
setValue(e);
|
||||
setEnabled(e);
|
||||
}}
|
||||
size="small"
|
||||
editNode={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { CustomTooltipProps } from "ag-grid-react";
|
||||
|
||||
export default function TableTooltipRender({ value }: CustomTooltipProps) {
|
||||
return (
|
||||
<div className="z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1">
|
||||
{value}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,22 +1,26 @@
|
|||
import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid
|
||||
import "ag-grid-community/styles/ag-theme-quartz.css"; // Optional Theme applied to the grid
|
||||
import { AgGridReact, AgGridReactProps } from "ag-grid-react";
|
||||
import { ElementRef, forwardRef, useCallback } from "react";
|
||||
import { ElementRef, forwardRef, useRef } from "react";
|
||||
import {
|
||||
DEFAULT_TABLE_ALERT_MSG,
|
||||
DEFAULT_TABLE_ALERT_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
|
||||
import { cn } from "../../utils/utils";
|
||||
import { cn, toTitleCase } from "../../utils/utils";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
||||
import ResetColumns from "./components/ResetColumns";
|
||||
import resetGrid from "./utils/reset-grid-columns";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
interface TableComponentProps extends AgGridReactProps {
|
||||
columnDefs: NonNullable<AgGridReactProps["columnDefs"]>;
|
||||
rowData: NonNullable<AgGridReactProps["rowData"]>;
|
||||
alertTitle?: string;
|
||||
alertDescription?: string;
|
||||
editable?: boolean | string[];
|
||||
}
|
||||
|
||||
const TableComponent = forwardRef<
|
||||
|
|
@ -31,7 +35,67 @@ const TableComponent = forwardRef<
|
|||
},
|
||||
ref,
|
||||
) => {
|
||||
let colDef = props.columnDefs.map((col, index) => {
|
||||
let newCol = {
|
||||
...col,
|
||||
headerName: toTitleCase(col.headerName),
|
||||
};
|
||||
if (index === props.columnDefs.length - 1) {
|
||||
newCol = {
|
||||
...newCol,
|
||||
resizable: false,
|
||||
};
|
||||
}
|
||||
if (props.onSelectionChanged && index === 0) {
|
||||
newCol = {
|
||||
...newCol,
|
||||
checkboxSelection: true,
|
||||
headerCheckboxSelection: true,
|
||||
headerCheckboxSelectionFilteredOnly: true,
|
||||
};
|
||||
}
|
||||
if (
|
||||
(typeof props.editable === "boolean" && props.editable) ||
|
||||
(Array.isArray(props.editable) &&
|
||||
props.editable.includes(newCol.headerName ?? ""))
|
||||
) {
|
||||
newCol = {
|
||||
...newCol,
|
||||
editable: true,
|
||||
};
|
||||
}
|
||||
return newCol;
|
||||
});
|
||||
const gridRef = useRef(null);
|
||||
// @ts-ignore
|
||||
const realRef = ref?.current ? ref : gridRef;
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
const initialColumnDefs = useRef(colDef);
|
||||
|
||||
const makeLastColumnNonResizable = (columnDefs) => {
|
||||
columnDefs.forEach((colDef, index) => {
|
||||
colDef.resizable = index !== columnDefs.length - 1;
|
||||
});
|
||||
return columnDefs;
|
||||
};
|
||||
|
||||
const onGridReady = (params) => {
|
||||
// @ts-ignore
|
||||
realRef.current = params;
|
||||
const updatedColumnDefs = makeLastColumnNonResizable([...colDef]);
|
||||
params.api.setGridOption("columnDefs", updatedColumnDefs);
|
||||
initialColumnDefs.current = params.api.getColumnDefs();
|
||||
if (props.onGridReady) props.onGridReady(params);
|
||||
};
|
||||
|
||||
const onColumnMoved = (params) => {
|
||||
const updatedColumnDefs = makeLastColumnNonResizable(
|
||||
params.columnApi.getAllGridColumns().map((col) => col.getColDef()),
|
||||
);
|
||||
params.api.setGridOption("columnDefs", updatedColumnDefs);
|
||||
if (props.onColumnMoved) props.onColumnMoved(params);
|
||||
};
|
||||
|
||||
if (props.rowData.length === 0) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center rounded-md border">
|
||||
|
|
@ -46,12 +110,12 @@ const TableComponent = forwardRef<
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
|
||||
"ag-theme-shadcn flex h-full flex-col",
|
||||
"relative",
|
||||
)} // applying the grid theme
|
||||
>
|
||||
<AgGridReact
|
||||
|
|
@ -60,8 +124,13 @@ const TableComponent = forwardRef<
|
|||
defaultColDef={{
|
||||
minWidth: 100,
|
||||
}}
|
||||
ref={ref}
|
||||
columnDefs={colDef}
|
||||
ref={realRef}
|
||||
pagination={true}
|
||||
onGridReady={onGridReady}
|
||||
onColumnMoved={onColumnMoved}
|
||||
/>
|
||||
<ResetColumns resetGrid={() => resetGrid(realRef, initialColumnDefs)} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
export default function resetGrid(ref, initialColumnDefs) {
|
||||
if (ref?.current && ref?.current.api) {
|
||||
ref.current.api.resetColumnState();
|
||||
if (initialColumnDefs.current) {
|
||||
const resetColumns = ref.current.api.applyColumnState({
|
||||
state: initialColumnDefs.current,
|
||||
applyOrder: true,
|
||||
});
|
||||
return resetColumns;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,20 +29,18 @@ export default function ToggleShadComponent({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
|
||||
<Switch
|
||||
id={id}
|
||||
data-testid={id}
|
||||
style={{
|
||||
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||
}}
|
||||
disabled={disabled}
|
||||
className=""
|
||||
checked={enabled}
|
||||
onCheckedChange={(isEnabled: boolean) => {
|
||||
setEnabled(isEnabled);
|
||||
}}
|
||||
></Switch>
|
||||
</div>
|
||||
<Switch
|
||||
id={id}
|
||||
data-testid={id}
|
||||
style={{
|
||||
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||
}}
|
||||
disabled={disabled}
|
||||
className=""
|
||||
checked={enabled}
|
||||
onCheckedChange={(isEnabled: boolean) => {
|
||||
setEnabled(isEnabled);
|
||||
}}
|
||||
></Switch>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|||
import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||
import * as React from "react";
|
||||
import { cn } from "../../utils/utils";
|
||||
import ShadTooltip from "../shadTooltipComponent";
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
|
|
@ -22,9 +23,14 @@ AccordionItem.displayName = "AccordionItem";
|
|||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
>(({ className, children, disabled, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger asChild ref={ref} {...props}>
|
||||
<AccordionPrimitive.Trigger
|
||||
disabled={disabled}
|
||||
asChild
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-1 cursor-pointer items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
|
||||
|
|
@ -32,7 +38,18 @@ const AccordionTrigger = React.forwardRef<
|
|||
)}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="h-4 w-4 font-bold text-primary transition-transform duration-200" />
|
||||
<ShadTooltip
|
||||
styleClasses="z-50"
|
||||
content={disabled ? "Empty" : ""}
|
||||
side="top"
|
||||
>
|
||||
<ChevronDownIcon
|
||||
className={cn(
|
||||
"h-4 w-4 font-bold transition-transform duration-200",
|
||||
disabled ? "text-muted-foreground" : "text-primary"
|
||||
)}
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import { Slot } from "@radix-ui/react-slot";
|
|||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
import { cn } from "../../utils/utils";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
|
||||
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
@ -19,6 +20,7 @@ const buttonVariants = cva(
|
|||
"border border-muted bg-muted text-secondary-foreground hover:bg-secondary-foreground/5",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "underline-offset-4 hover:underline text-primary",
|
||||
none: "",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 py-2 px-4",
|
||||
|
|
@ -26,6 +28,7 @@ const buttonVariants = cva(
|
|||
xs: "py-0.5 px-3 rounded-md",
|
||||
lg: "h-11 px-8 rounded-md",
|
||||
icon: "py-1 px-1 rounded-md",
|
||||
none: "",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
@ -39,6 +42,7 @@ export interface ButtonProps
|
|||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
function toTitleCase(text: string) {
|
||||
|
|
@ -49,19 +53,49 @@ function toTitleCase(text: string) {
|
|||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, children, ...props }, ref) => {
|
||||
(
|
||||
{
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
loading,
|
||||
type,
|
||||
disabled,
|
||||
asChild = false,
|
||||
children,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
let newChildren = children;
|
||||
if (typeof children === "string") {
|
||||
newChildren = toTitleCase(children);
|
||||
}
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
children={newChildren}
|
||||
{...props}
|
||||
/>
|
||||
<>
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
disabled={loading || disabled}
|
||||
{...(asChild ? {} : { type: type || "button" })}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{loading ? (
|
||||
<span className="relative">
|
||||
<span className="invisible">{newChildren}</span>
|
||||
<span className="absolute inset-0 flex items-center justify-center">
|
||||
<ForwardedIconComponent
|
||||
name={"Loader2"}
|
||||
className={"animate-spin"}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
newChildren
|
||||
)}
|
||||
</Comp>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const Card = React.forwardRef<
|
|||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex flex-col justify-between rounded-lg border bg-muted text-card-foreground shadow-sm transition-all hover:shadow-lg",
|
||||
"flex flex-col justify-between rounded-lg border bg-muted text-card-foreground shadow-sm transition-all",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,7 @@ function RefreshButton({
|
|||
|
||||
// icon class name should take into account the disabled state and the loading state
|
||||
const disabledIconTextClass = disabled ? "text-muted-foreground" : "";
|
||||
const iconClassName = cn(
|
||||
"h-4 w-4",
|
||||
isLoading ? "animate-spin" : "animate-wiggle",
|
||||
disabledIconTextClass
|
||||
);
|
||||
const iconClassName = cn("h-4 w-4 animate-wiggle", disabledIconTextClass);
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -44,10 +40,11 @@ function RefreshButton({
|
|||
className={classNames}
|
||||
onClick={handleClick}
|
||||
id={id}
|
||||
loading={isLoading}
|
||||
>
|
||||
{button_text && <span className="mr-1">{button_text}</span>}
|
||||
<IconComponent
|
||||
name={isLoading ? "Loader2" : "RefreshCcw"}
|
||||
name={"RefreshCcw"}
|
||||
className={iconClassName}
|
||||
id={id + "-icon"}
|
||||
/>
|
||||
|
|
|
|||
45
src/frontend/src/components/ui/toggle.tsx
Normal file
45
src/frontend/src/components/ui/toggle.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
"use client";
|
||||
|
||||
import * as TogglePrimitive from "@radix-ui/react-toggle";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-transparent",
|
||||
outline:
|
||||
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-3",
|
||||
sm: "h-9 px-2.5",
|
||||
lg: "h-11 px-5",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const Toggle = React.forwardRef<
|
||||
React.ElementRef<typeof TogglePrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, variant, size, ...props }, ref) => (
|
||||
<TogglePrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(toggleVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
Toggle.displayName = TogglePrimitive.Root.displayName;
|
||||
|
||||
export { Toggle, toggleVariants };
|
||||
|
|
@ -28,4 +28,26 @@ const TooltipContent = React.forwardRef<
|
|||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
||||
const TooltipContentWithoutPortal = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TooltipContentWithoutPortal.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipContentWithoutPortal,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export const USER_EDIT_ERROR_ALERT = "Error on edit user";
|
|||
export const USER_ADD_ERROR_ALERT = "Error when adding new user";
|
||||
export const SIGNIN_ERROR_ALERT = "Error signing in";
|
||||
export const DEL_KEY_ERROR_ALERT = "Error on delete key";
|
||||
export const DEL_KEY_ERROR_ALERT_PLURAL = "Error on delete keys";
|
||||
export const UPLOAD_ERROR_ALERT = "Error uploading file";
|
||||
export const WRONG_FILE_ERROR_ALERT = "Invalid file type";
|
||||
export const UPLOAD_ALERT_LIST = "Please upload a JSON file";
|
||||
|
|
@ -54,6 +55,7 @@ export const USER_DEL_SUCCESS_ALERT = "Success! User deleted!";
|
|||
export const USER_EDIT_SUCCESS_ALERT = "Success! User edited!";
|
||||
export const USER_ADD_SUCCESS_ALERT = "Success! New user added!";
|
||||
export const DEL_KEY_SUCCESS_ALERT = "Success! Key deleted!";
|
||||
export const DEL_KEY_SUCCESS_ALERT_PLURAL = "Success! Keys deleted!";
|
||||
export const FLOW_BUILD_SUCCESS_ALERT = `Flow built successfully`;
|
||||
export const SAVE_SUCCESS_ALERT = "Changes saved successfully!";
|
||||
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ export const CONTROL_PATCH_USER_STATE = {
|
|||
password: "",
|
||||
cnfPassword: "",
|
||||
gradient: "",
|
||||
apikey: "",
|
||||
};
|
||||
|
||||
export const CONTROL_LOGIN_STATE = {
|
||||
|
|
@ -612,11 +613,8 @@ export const FETCH_ERROR_DESCRIPION =
|
|||
|
||||
export const SIGN_UP_SUCCESS = "Account created! Await admin activation. ";
|
||||
|
||||
export const API_PAGE_PARAGRAPH_1 =
|
||||
"Your secret API keys are listed below. Please note that we do not display your secret API keys again after you generate them.";
|
||||
|
||||
export const API_PAGE_PARAGRAPH_2 =
|
||||
"Do not share your API key with others, or expose it in the browser or other client-side code.";
|
||||
export const API_PAGE_PARAGRAPH =
|
||||
"Your secret API keys are listed below. Do not share your API key with others, or expose it in the browser or other client-side code.";
|
||||
|
||||
export const API_PAGE_USER_KEYS =
|
||||
"This user does not have any keys assigned at the moment.";
|
||||
|
|
@ -670,7 +668,7 @@ export const ZERO_NOTIFICATIONS = "No new notifications";
|
|||
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
||||
|
||||
export const ALERT_SAVE_WITH_API =
|
||||
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
|
||||
"Caution: Unchecking this box only removes API keys from fields specifically designated for API keys.";
|
||||
|
||||
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
||||
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
||||
|
|
@ -738,3 +736,5 @@ export const DEFAULT_TABLE_ALERT_MSG = `Oops! It seems there's no data to displa
|
|||
export const DEFAULT_TABLE_ALERT_TITLE = "No Data Available";
|
||||
|
||||
export const LOCATIONS_TO_RETURN = ["/flow/", "/settings/"];
|
||||
|
||||
export const MAX_BATCH_SIZE = 50;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import axios, { AxiosError, AxiosInstance } from "axios";
|
|||
import { useContext, useEffect } from "react";
|
||||
import { Cookies } from "react-cookie";
|
||||
import { renewAccessToken } from ".";
|
||||
import { AUTHORIZED_DUPLICATE_REQUESTS } from "../../constants/constants";
|
||||
import { BuildStatus } from "../../constants/enums";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import { checkDuplicateRequestAndStoreRequest } from "./helpers/check-duplicate-requests";
|
||||
|
||||
// Create a new Axios instance
|
||||
const api: AxiosInstance = axios.create({
|
||||
|
|
@ -81,28 +81,12 @@ function ApiInterceptor() {
|
|||
// Request interceptor to add access token to every request
|
||||
const requestInterceptor = api.interceptors.request.use(
|
||||
(config) => {
|
||||
const lastUrl = localStorage.getItem("lastUrlCalled");
|
||||
const lastMethodCalled = localStorage.getItem("lastMethodCalled");
|
||||
const checkRequest = checkDuplicateRequestAndStoreRequest(config);
|
||||
|
||||
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
|
||||
config?.url!.includes(request),
|
||||
);
|
||||
|
||||
if (
|
||||
config?.url === lastUrl &&
|
||||
!isContained &&
|
||||
lastMethodCalled === config.method
|
||||
) {
|
||||
return Promise.reject("Duplicate request");
|
||||
if (!checkRequest) {
|
||||
return Promise.reject("Duplicate request.");
|
||||
}
|
||||
|
||||
localStorage.setItem("lastUrlCalled", config.url ?? "");
|
||||
localStorage.setItem("lastMethodCalled", config.method ?? "");
|
||||
localStorage.setItem(
|
||||
"lastRequestData",
|
||||
JSON.stringify(config.data) ?? "",
|
||||
);
|
||||
|
||||
const accessToken = cookies.get("access_token_lf");
|
||||
if (accessToken && !isAuthorizedURL(config?.url)) {
|
||||
config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { AUTHORIZED_DUPLICATE_REQUESTS } from "../../../constants/constants";
|
||||
|
||||
export function checkDuplicateRequestAndStoreRequest(config) {
|
||||
const lastUrl = localStorage.getItem("lastUrlCalled");
|
||||
const lastMethodCalled = localStorage.getItem("lastMethodCalled");
|
||||
const lastRequestTime = localStorage.getItem("lastRequestTime");
|
||||
|
||||
const currentTime = Date.now();
|
||||
|
||||
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
|
||||
config?.url!.includes(request)
|
||||
);
|
||||
|
||||
if (
|
||||
config?.url === lastUrl &&
|
||||
!isContained &&
|
||||
lastMethodCalled === config.method &&
|
||||
lastMethodCalled === "get" && // Assuming you want to check only for GET requests
|
||||
lastRequestTime &&
|
||||
currentTime - parseInt(lastRequestTime, 10) < 800
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
localStorage.setItem("lastUrlCalled", config.url ?? "");
|
||||
localStorage.setItem("lastMethodCalled", config.method ?? "");
|
||||
localStorage.setItem("lastRequestTime", currentTime.toString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { ColDef, ColGroupDef } from "ag-grid-community";
|
||||
import { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import { Edge, Node, ReactFlowJsonObject } from "reactflow";
|
||||
import { BASE_URL_API } from "../../constants/constants";
|
||||
import { BASE_URL_API, MAX_BATCH_SIZE } from "../../constants/constants";
|
||||
import { api } from "../../controllers/API/api";
|
||||
import {
|
||||
APIObjectType,
|
||||
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from "../../types/api/index";
|
||||
import { UserInputType } from "../../types/components";
|
||||
import { FlowStyleType, FlowType } from "../../types/flow";
|
||||
import { Message } from "../../types/messages";
|
||||
import { StoreComponentResponse } from "../../types/store";
|
||||
import { FlowPoolType } from "../../types/zustand/flow";
|
||||
import { extractColumnsFromRows } from "../../utils/utils";
|
||||
|
|
@ -1002,12 +1003,41 @@ export async function deleteFlowPool(
|
|||
return await api.delete(`${BASE_URL_API}monitor/builds`, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple flow components by their IDs.
|
||||
* @param flowIds - An array of flow IDs to be deleted.
|
||||
* @param token - The authorization token for the API request.
|
||||
* @returns A promise that resolves to an array of AxiosResponse objects representing the delete responses.
|
||||
*/
|
||||
export async function multipleDeleteFlowsComponents(
|
||||
flowIds: string[]
|
||||
): Promise<AxiosResponse<any>> {
|
||||
return await api.post(`${BASE_URL_API}flows/multiple_delete/`, {
|
||||
flow_ids: flowIds,
|
||||
});
|
||||
): Promise<AxiosResponse<any>[]> {
|
||||
const batches: string[][] = [];
|
||||
|
||||
// Split the flowIds into batches
|
||||
for (let i = 0; i < flowIds.length; i += MAX_BATCH_SIZE) {
|
||||
batches.push(flowIds.slice(i, i + MAX_BATCH_SIZE));
|
||||
}
|
||||
|
||||
// Function to delete a batch of flow IDs
|
||||
const deleteBatch = async (batch: string[]): Promise<AxiosResponse<any>> => {
|
||||
try {
|
||||
return await api.delete(`${BASE_URL_API}flows/`, {
|
||||
data: batch,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error deleting flows:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Execute all delete requests
|
||||
const responses: Promise<AxiosResponse<any>>[] = batches.map((batch) =>
|
||||
deleteBatch(batch)
|
||||
);
|
||||
|
||||
// Return the responses after all requests are completed
|
||||
return Promise.all(responses);
|
||||
}
|
||||
|
||||
export async function getTransactionTable(
|
||||
|
|
@ -1026,16 +1056,47 @@ export async function getTransactionTable(
|
|||
}
|
||||
|
||||
export async function getMessagesTable(
|
||||
id: string,
|
||||
mode: "intersection" | "union",
|
||||
id?: string,
|
||||
excludedFields?: string[],
|
||||
params = {}
|
||||
): Promise<{ rows: Array<object>; columns: Array<ColDef | ColGroupDef> }> {
|
||||
): Promise<{ rows: Array<Message>; columns: Array<ColDef | ColGroupDef> }> {
|
||||
const config = {};
|
||||
config["params"] = { flow_id: id };
|
||||
if (id) {
|
||||
config["params"] = { flow_id: id };
|
||||
}
|
||||
if (params) {
|
||||
config["params"] = { ...config["params"], ...params };
|
||||
}
|
||||
const rows = await api.get(`${BASE_URL_API}monitor/messages`, config);
|
||||
const columns = extractColumnsFromRows(rows.data, mode);
|
||||
const columns = extractColumnsFromRows(rows.data, mode, excludedFields);
|
||||
return { rows: rows.data, columns };
|
||||
}
|
||||
|
||||
export async function getSessions(id?: string): Promise<Array<string>> {
|
||||
const config = {};
|
||||
if (id) {
|
||||
config["params"] = { flow_id: id };
|
||||
}
|
||||
const rows = await api.get(`${BASE_URL_API}monitor/messages`, config);
|
||||
const sessions = new Set<string>();
|
||||
rows.data.forEach((row) => {
|
||||
sessions.add(row.session_id);
|
||||
});
|
||||
return Array.from(sessions);
|
||||
}
|
||||
|
||||
export async function deleteMessagesFn(ids: number[]) {
|
||||
try {
|
||||
return await api.delete(`${BASE_URL_API}monitor/messages`, {
|
||||
data: ids,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error deleting flows:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateMessageApi(data: Message) {
|
||||
return await api.post(`${BASE_URL_API}monitor/messages/${data.index}`, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ const SvgBotMessageSquare = (props) => (
|
|||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="lucide lucide-bot-message-square"
|
||||
{...props}
|
||||
>
|
||||
|
|
|
|||
25
src/frontend/src/icons/Streamlit/SvgStreamlit.jsx
Normal file
25
src/frontend/src/icons/Streamlit/SvgStreamlit.jsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export default function SvgStreamlit(props) {
|
||||
return (
|
||||
<svg
|
||||
width="301"
|
||||
height="165"
|
||||
viewBox="0 0 301 165"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M150.731 101.547L98.1387 73.7471L6.84674 25.4969C6.7634 25.4136 6.59674 25.4136 6.51341 25.4136C3.18007 23.8303 -0.236608 27.1636 1.0134 30.497L47.5302 149.139L47.5385 149.164C47.5885 149.281 47.6302 149.397 47.6802 149.514C49.5885 153.939 53.7552 156.672 58.2886 157.747C58.6719 157.831 58.9461 157.906 59.4064 157.998C59.8645 158.1 60.5052 158.239 61.0552 158.281C61.1469 158.289 61.2302 158.289 61.3219 158.297H61.3886C61.4552 158.306 61.5219 158.306 61.5886 158.314H61.6802C61.7386 158.322 61.8052 158.322 61.8636 158.322H61.9719C62.0386 158.331 62.1052 158.331 62.1719 158.331V158.331C121.084 164.754 180.519 164.754 239.431 158.331V158.331C240.139 158.331 240.831 158.297 241.497 158.231C241.714 158.206 241.922 158.181 242.131 158.156C242.156 158.147 242.189 158.147 242.214 158.139C242.356 158.122 242.497 158.097 242.639 158.072C242.847 158.047 243.056 158.006 243.264 157.964C243.681 157.872 243.87 157.806 244.436 157.611C245.001 157.417 245.94 157.077 246.527 156.794C247.115 156.511 247.522 156.239 248.014 155.931C248.622 155.547 249.201 155.155 249.788 154.715C250.041 154.521 250.214 154.397 250.397 154.222L250.297 154.164L150.731 101.547Z"
|
||||
fill="#FF4B4B"
|
||||
/>
|
||||
<path
|
||||
d="M294.766 25.4981H294.683L203.357 73.7483L254.124 149.357L300.524 30.4981V30.3315C301.691 26.8314 298.108 23.6648 294.766 25.4981"
|
||||
fill="#7D353B"
|
||||
/>
|
||||
<path
|
||||
d="M155.598 2.55572C153.264 -0.852624 148.181 -0.852624 145.931 2.55572L98.1389 73.7477L150.731 101.548L250.398 154.222C251.024 153.609 251.526 153.012 252.056 152.381C252.806 151.456 253.506 150.465 254.123 149.356L203.356 73.7477L155.598 2.55572Z"
|
||||
fill="#BD4043"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
8
src/frontend/src/icons/Streamlit/index.tsx
Normal file
8
src/frontend/src/icons/Streamlit/index.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import SvgStreamlit from "./SvgStreamlit";
|
||||
|
||||
export const Streamlit = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
|
||||
(props, ref) => {
|
||||
return <SvgStreamlit className="icon" ref={ref} {...props} />;
|
||||
}
|
||||
);
|
||||
|
|
@ -27,7 +27,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => {
|
|||
getFoldersApi(true);
|
||||
setOpen(false);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addFolder(data).then(
|
||||
|
|
@ -42,7 +42,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => {
|
|||
setErrorData({
|
||||
title: `Error creating folder.`,
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue