Merge branch 'form_io' into python_custom_node_component
This commit is contained in:
commit
320989870e
51 changed files with 1012 additions and 606 deletions
|
|
@ -71,30 +71,68 @@ def validate_prompt(template: str):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise ValueError(str(exc)) from exc
|
raise ValueError(str(exc)) from exc
|
||||||
|
|
||||||
# if len(input_variables) > 1:
|
|
||||||
# # If there's more than one input variable
|
|
||||||
|
|
||||||
return input_variables
|
return input_variables
|
||||||
|
|
||||||
|
|
||||||
def check_input_variables(input_variables: list):
|
def check_input_variables(input_variables: list):
|
||||||
invalid_chars = []
|
invalid_chars = []
|
||||||
fixed_variables = []
|
fixed_variables = []
|
||||||
|
wrong_variables = []
|
||||||
|
empty_variables = []
|
||||||
for variable in input_variables:
|
for variable in input_variables:
|
||||||
new_var = variable
|
new_var = variable
|
||||||
for char in INVALID_CHARACTERS:
|
|
||||||
if char in variable:
|
# if variable is empty, then we should add that to the wrong variables
|
||||||
invalid_chars.append(char)
|
if not variable:
|
||||||
new_var = new_var.replace(char, "")
|
empty_variables.append(variable)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# if variable starts with a number we should add that to the invalid chars
|
||||||
|
# and wrong variables
|
||||||
|
if variable[0].isdigit():
|
||||||
|
invalid_chars.append(variable[0])
|
||||||
|
new_var = new_var.replace(variable[0], "")
|
||||||
|
wrong_variables.append(variable)
|
||||||
|
else:
|
||||||
|
for char in INVALID_CHARACTERS:
|
||||||
|
if char in variable:
|
||||||
|
invalid_chars.append(char)
|
||||||
|
new_var = new_var.replace(char, "")
|
||||||
|
wrong_variables.append(variable)
|
||||||
fixed_variables.append(new_var)
|
fixed_variables.append(new_var)
|
||||||
if new_var != variable:
|
|
||||||
input_variables.remove(variable)
|
|
||||||
input_variables.append(new_var)
|
|
||||||
# If any of the input_variables is not in the fixed_variables, then it means that
|
# If any of the input_variables is not in the fixed_variables, then it means that
|
||||||
# there are invalid characters in the input_variables
|
# there are invalid characters in the input_variables
|
||||||
if any(var not in fixed_variables for var in input_variables):
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid input variables: {input_variables}. Please, use something like {fixed_variables} instead."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
if any(var not in fixed_variables for var in input_variables):
|
||||||
|
error_message = build_error_message(
|
||||||
|
input_variables,
|
||||||
|
invalid_chars,
|
||||||
|
wrong_variables,
|
||||||
|
fixed_variables,
|
||||||
|
empty_variables,
|
||||||
|
)
|
||||||
|
raise ValueError(error_message)
|
||||||
return input_variables
|
return input_variables
|
||||||
|
|
||||||
|
|
||||||
|
def build_error_message(
|
||||||
|
input_variables, invalid_chars, wrong_variables, fixed_variables, empty_variables
|
||||||
|
):
|
||||||
|
input_variables_str = ", ".join([f"'{var}'" for var in input_variables])
|
||||||
|
error_string = f"Invalid input variables: {input_variables_str}. "
|
||||||
|
|
||||||
|
if wrong_variables and invalid_chars:
|
||||||
|
# fix the wrong variables replacing invalid chars and find them in the fixed variables
|
||||||
|
error_string_vars = "You can fix them by replacing the invalid characters: "
|
||||||
|
wvars = wrong_variables.copy()
|
||||||
|
for i, wrong_var in enumerate(wvars):
|
||||||
|
for char in invalid_chars:
|
||||||
|
wrong_var = wrong_var.replace(char, "")
|
||||||
|
if wrong_var in fixed_variables:
|
||||||
|
error_string_vars += f"'{wrong_variables[i]}' -> '{wrong_var}'"
|
||||||
|
error_string += error_string_vars
|
||||||
|
elif empty_variables:
|
||||||
|
error_string += f" There are {len(empty_variables)} empty variable{'s' if len(empty_variables) > 1 else ''}."
|
||||||
|
elif len(set(fixed_variables)) != len(fixed_variables):
|
||||||
|
error_string += "There are duplicate variables."
|
||||||
|
return error_string
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,107 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
|
from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
|
||||||
|
|
||||||
from langflow.api.v1.schemas import ChatResponse
|
from langflow.api.v1.schemas import ChatResponse
|
||||||
|
|
||||||
|
|
||||||
|
from typing import Any, Dict, List, Union
|
||||||
|
from fastapi import WebSocket
|
||||||
|
|
||||||
|
|
||||||
|
from langchain.schema import AgentAction, LLMResult, AgentFinish
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py
|
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py
|
||||||
class AsyncStreamingLLMCallbackHandler(AsyncCallbackHandler):
|
class AsyncStreamingLLMCallbackHandler(AsyncCallbackHandler):
|
||||||
"""Callback handler for streaming LLM responses."""
|
"""Callback handler for streaming LLM responses."""
|
||||||
|
|
||||||
def __init__(self, websocket):
|
def __init__(self, websocket: WebSocket):
|
||||||
self.websocket = websocket
|
self.websocket = websocket
|
||||||
|
|
||||||
async def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
async def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
||||||
resp = ChatResponse(message=token, type="stream", intermediate_steps="")
|
resp = ChatResponse(message=token, type="stream", intermediate_steps="")
|
||||||
await self.websocket.send_json(resp.dict())
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_llm_start(
|
||||||
|
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when LLM starts running."""
|
||||||
|
|
||||||
|
async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any:
|
||||||
|
"""Run when LLM ends running."""
|
||||||
|
|
||||||
|
async def on_llm_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when LLM errors."""
|
||||||
|
|
||||||
|
async def on_chain_start(
|
||||||
|
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when chain starts running."""
|
||||||
|
|
||||||
|
async def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:
|
||||||
|
"""Run when chain ends running."""
|
||||||
|
|
||||||
|
async def on_chain_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when chain errors."""
|
||||||
|
|
||||||
|
async def on_tool_start(
|
||||||
|
self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when tool starts running."""
|
||||||
|
resp = ChatResponse(
|
||||||
|
message="",
|
||||||
|
type="stream",
|
||||||
|
intermediate_steps=f"Tool input: {input_str}",
|
||||||
|
)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_tool_end(self, output: str, **kwargs: Any) -> Any:
|
||||||
|
"""Run when tool ends running."""
|
||||||
|
resp = ChatResponse(
|
||||||
|
message="",
|
||||||
|
type="stream",
|
||||||
|
intermediate_steps=f"Tool output: {output}",
|
||||||
|
)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_tool_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when tool errors."""
|
||||||
|
|
||||||
|
async def on_text(self, text: str, **kwargs: Any) -> Any:
|
||||||
|
"""Run on arbitrary text."""
|
||||||
|
# This runs when first sending the prompt
|
||||||
|
# to the LLM, adding it will send the final prompt
|
||||||
|
# to the frontend
|
||||||
|
|
||||||
|
async def on_agent_action(self, action: AgentAction, **kwargs: Any):
|
||||||
|
log = f"Thought: {action.log}"
|
||||||
|
# if there are line breaks, split them and send them
|
||||||
|
# as separate messages
|
||||||
|
if "\n" in log:
|
||||||
|
logs = log.split("\n")
|
||||||
|
for log in logs:
|
||||||
|
resp = ChatResponse(message="", type="stream", intermediate_steps=log)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
else:
|
||||||
|
resp = ChatResponse(message="", type="stream", intermediate_steps=log)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any:
|
||||||
|
"""Run on agent end."""
|
||||||
|
resp = ChatResponse(
|
||||||
|
message="",
|
||||||
|
type="stream",
|
||||||
|
intermediate_steps=finish.log,
|
||||||
|
)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
|
||||||
class StreamingLLMCallbackHandler(BaseCallbackHandler):
|
class StreamingLLMCallbackHandler(BaseCallbackHandler):
|
||||||
"""Callback handler for streaming LLM responses."""
|
"""Callback handler for streaming LLM responses."""
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,13 @@ async def stream_build(flow_id: str):
|
||||||
input_keys_response = build_input_keys_response(
|
input_keys_response = build_input_keys_response(
|
||||||
langchain_object, artifacts
|
langchain_object, artifacts
|
||||||
)
|
)
|
||||||
yield str(StreamData(event="message", data=input_keys_response))
|
else:
|
||||||
|
input_keys_response = {
|
||||||
|
"input_keys": {},
|
||||||
|
"memory_keys": [],
|
||||||
|
"handle_keys": [],
|
||||||
|
}
|
||||||
|
yield str(StreamData(event="message", data=input_keys_response))
|
||||||
|
|
||||||
chat_manager.set_cache(flow_id, langchain_object)
|
chat_manager.set_cache(flow_id, langchain_object)
|
||||||
# We need to reset the chat history
|
# We need to reset the chat history
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,14 @@ def add_new_variables_to_template(input_variables, prompt_request):
|
||||||
advanced=False,
|
advanced=False,
|
||||||
multiline=True,
|
multiline=True,
|
||||||
input_types=["Document", "BaseOutputParser"],
|
input_types=["Document", "BaseOutputParser"],
|
||||||
|
value="", # Set the value to empty string
|
||||||
)
|
)
|
||||||
if variable in prompt_request.frontend_node.template:
|
if variable in prompt_request.frontend_node.template:
|
||||||
# Set the new field with the old value
|
# Set the new field with the old value
|
||||||
template_field.value = prompt_request.frontend_node.template[variable][
|
template_field.value = prompt_request.frontend_node.template[variable][
|
||||||
"value"
|
"value"
|
||||||
]
|
]
|
||||||
|
|
||||||
prompt_request.frontend_node.template[variable] = template_field.to_dict()
|
prompt_request.frontend_node.template[variable] = template_field.to_dict()
|
||||||
|
|
||||||
# Check if variable is not already in the list before appending
|
# Check if variable is not already in the list before appending
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ async def process_graph(
|
||||||
try:
|
try:
|
||||||
logger.debug("Generating result and thought")
|
logger.debug("Generating result and thought")
|
||||||
result, intermediate_steps = await get_result_and_steps(
|
result, intermediate_steps = await get_result_and_steps(
|
||||||
langchain_object, chat_inputs.message or "", websocket=websocket
|
langchain_object, chat_inputs.message, websocket=websocket
|
||||||
)
|
)
|
||||||
logger.debug("Generated result and intermediate_steps")
|
logger.debug("Generated result and intermediate_steps")
|
||||||
return result, intermediate_steps
|
return result, intermediate_steps
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,6 @@ memories:
|
||||||
prompts:
|
prompts:
|
||||||
PromptTemplate:
|
PromptTemplate:
|
||||||
documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
|
documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
|
||||||
ZeroShotPrompt:
|
|
||||||
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
|
|
||||||
textsplitters:
|
textsplitters:
|
||||||
CharacterTextSplitter:
|
CharacterTextSplitter:
|
||||||
documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
|
documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ from langflow.template import frontend_node
|
||||||
|
|
||||||
# These should always be instantiated
|
# These should always be instantiated
|
||||||
CUSTOM_NODES = {
|
CUSTOM_NODES = {
|
||||||
"prompts": {
|
# "prompts": {
|
||||||
"ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode(),
|
# "ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode(),
|
||||||
},
|
# },
|
||||||
"tools": {
|
"tools": {
|
||||||
"PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(),
|
"PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(),
|
||||||
"PythonFunction": frontend_node.tools.PythonFunctionNode(),
|
"PythonFunction": frontend_node.tools.PythonFunctionNode(),
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,8 @@ class Vertex:
|
||||||
# and return the instance
|
# and return the instance
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if self.base_type is None:
|
||||||
|
raise ValueError(f"Base type for node {self.vertex_type} not found")
|
||||||
result = loading.instantiate_class(
|
result = loading.instantiate_class(
|
||||||
node_type=self.vertex_type,
|
node_type=self.vertex_type,
|
||||||
base_type=self.base_type,
|
base_type=self.base_type,
|
||||||
|
|
@ -224,4 +226,5 @@ class Vertex:
|
||||||
return id(self)
|
return id(self)
|
||||||
|
|
||||||
def _built_object_repr(self):
|
def _built_object_repr(self):
|
||||||
return repr(self._built_object)
|
# Add a message with an emoji, stars for sucess,
|
||||||
|
return "Built sucessfully ✨" if self._built_object else "Failed to build 😵💫"
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,15 @@ class PromptVertex(Vertex):
|
||||||
self._build()
|
self._build()
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
def _built_object_repr(self):
|
||||||
|
if self.artifacts and hasattr(self._built_object, "format"):
|
||||||
|
# We'll build the prompt with the artifacts
|
||||||
|
# to show the user what the prompt looks like
|
||||||
|
# with the variables filled in
|
||||||
|
return self._built_object.format(**self.artifacts)
|
||||||
|
else:
|
||||||
|
return super()._built_object_repr()
|
||||||
|
|
||||||
|
|
||||||
class OutputParserVertex(Vertex):
|
class OutputParserVertex(Vertex):
|
||||||
def __init__(self, data: Dict):
|
def __init__(self, data: Dict):
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,20 @@ from langflow.custom.customs import get_custom_nodes
|
||||||
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
||||||
from langflow.interface.base import LangChainTypeCreator
|
from langflow.interface.base import LangChainTypeCreator
|
||||||
from langflow.settings import settings
|
from langflow.settings import settings
|
||||||
|
from langflow.template.frontend_node.agents import AgentFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
|
|
||||||
|
|
||||||
class AgentCreator(LangChainTypeCreator):
|
class AgentCreator(LangChainTypeCreator):
|
||||||
type_name: str = "agents"
|
type_name: str = "agents"
|
||||||
|
|
||||||
|
from_method_nodes = {"ZeroShotAgent": "from_llm_and_tools"}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frontend_node_class(self) -> type[AgentFrontendNode]:
|
||||||
|
return AgentFrontendNode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type_to_loader_dict(self) -> Dict:
|
def type_to_loader_dict(self) -> Dict:
|
||||||
if self.type_dict is None:
|
if self.type_dict is None:
|
||||||
|
|
@ -27,6 +34,13 @@ class AgentCreator(LangChainTypeCreator):
|
||||||
try:
|
try:
|
||||||
if name in get_custom_nodes(self.type_name).keys():
|
if name in get_custom_nodes(self.type_name).keys():
|
||||||
return get_custom_nodes(self.type_name)[name]
|
return get_custom_nodes(self.type_name)[name]
|
||||||
|
elif name in self.from_method_nodes:
|
||||||
|
return build_template_from_method(
|
||||||
|
name,
|
||||||
|
type_to_cls_dict=self.type_to_loader_dict,
|
||||||
|
add_function=True,
|
||||||
|
method_name=self.from_method_nodes[name],
|
||||||
|
)
|
||||||
return build_template_from_class(
|
return build_template_from_class(
|
||||||
name, self.type_to_loader_dict, add_function=True
|
name, self.type_to_loader_dict, add_function=True
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ from langflow.interface.importing.utils import (
|
||||||
import_by_type,
|
import_by_type,
|
||||||
)
|
)
|
||||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||||
|
from langflow.interface.importing.utils import get_function, import_by_type
|
||||||
|
from langflow.interface.agents.base import agent_creator
|
||||||
from langflow.interface.toolkits.base import toolkits_creator
|
from langflow.interface.toolkits.base import toolkits_creator
|
||||||
from langflow.interface.chains.base import chain_creator
|
from langflow.interface.chains.base import chain_creator
|
||||||
from langflow.interface.output_parsers.base import output_parser_creator
|
from langflow.interface.output_parsers.base import output_parser_creator
|
||||||
|
|
@ -65,7 +67,7 @@ def convert_kwargs(params):
|
||||||
|
|
||||||
def instantiate_based_on_type(class_object, base_type, node_type, params):
|
def instantiate_based_on_type(class_object, base_type, node_type, params):
|
||||||
if base_type == "agents":
|
if base_type == "agents":
|
||||||
return instantiate_agent(class_object, params)
|
return instantiate_agent(node_type, class_object, params)
|
||||||
elif base_type == "prompts":
|
elif base_type == "prompts":
|
||||||
return instantiate_prompt(node_type, class_object, params)
|
return instantiate_prompt(node_type, class_object, params)
|
||||||
elif base_type == "tools":
|
elif base_type == "tools":
|
||||||
|
|
@ -122,6 +124,12 @@ def instantiate_llm(node_type, class_object, params: Dict):
|
||||||
|
|
||||||
|
|
||||||
def instantiate_memory(node_type, class_object, params):
|
def instantiate_memory(node_type, class_object, params):
|
||||||
|
# process input_key and output_key to remove them if
|
||||||
|
# they are empty strings
|
||||||
|
for key in ["input_key", "output_key"]:
|
||||||
|
if key in params and not params[key]:
|
||||||
|
params.pop(key)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
||||||
params["retriever"] = params["retriever"].as_retriever()
|
params["retriever"] = params["retriever"].as_retriever()
|
||||||
|
|
@ -164,7 +172,16 @@ def instantiate_chains(node_type, class_object: Type[Chain], params: Dict):
|
||||||
return class_object(**params)
|
return class_object(**params)
|
||||||
|
|
||||||
|
|
||||||
def instantiate_agent(class_object: Type[agent_module.Agent], params: Dict):
|
def instantiate_agent(node_type, class_object: Type[agent_module.Agent], params: Dict):
|
||||||
|
if node_type in agent_creator.from_method_nodes:
|
||||||
|
method = agent_creator.from_method_nodes[node_type]
|
||||||
|
if class_method := getattr(class_object, method, None):
|
||||||
|
agent = class_method(**params)
|
||||||
|
tools = params.get("tools", [])
|
||||||
|
return AgentExecutor.from_agent_and_tools(
|
||||||
|
agent=agent,
|
||||||
|
tools=tools,
|
||||||
|
)
|
||||||
return load_agent_executor(class_object, params)
|
return load_agent_executor(class_object, params)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ class ToolCreator(LangChainTypeCreator):
|
||||||
def get_signature(self, name: str) -> Optional[Dict]:
|
def get_signature(self, name: str) -> Optional[Dict]:
|
||||||
"""Get the signature of a tool."""
|
"""Get the signature of a tool."""
|
||||||
|
|
||||||
base_classes = ["Tool"]
|
base_classes = ["Tool", "BaseTool"]
|
||||||
fields = []
|
fields = []
|
||||||
params = []
|
params = []
|
||||||
tool_params = {}
|
tool_params = {}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,17 @@ NON_CHAT_AGENTS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AgentFrontendNode(FrontendNode):
|
||||||
|
@staticmethod
|
||||||
|
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||||
|
if field.name in ["suffix", "prefix"]:
|
||||||
|
field.show = True
|
||||||
|
if field.name == "Tools" and name == "ZeroShotAgent":
|
||||||
|
# field.
|
||||||
|
field.type_name = "BaseTool"
|
||||||
|
field.is_list = True
|
||||||
|
|
||||||
|
|
||||||
class SQLAgentNode(FrontendNode):
|
class SQLAgentNode(FrontendNode):
|
||||||
name: str = "SQLAgent"
|
name: str = "SQLAgent"
|
||||||
template: Template = Template(
|
template: Template = Template(
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ class ToolNode(FrontendNode):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
description: str = "Converts a chain, agent or function into a tool."
|
description: str = "Converts a chain, agent or function into a tool."
|
||||||
base_classes: list[str] = ["Tool"]
|
base_classes: list[str] = ["Tool", "BaseTool"]
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return super().to_dict()
|
return super().to_dict()
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,11 @@ def format_dict(d, name: Optional[str] = None):
|
||||||
|
|
||||||
# Check for list type
|
# Check for list type
|
||||||
if "List" in _type or "Sequence" in _type or "Set" in _type:
|
if "List" in _type or "Sequence" in _type or "Set" in _type:
|
||||||
_type = _type.replace("List[", "")[:-1]
|
_type = (
|
||||||
|
_type.replace("List[", "")
|
||||||
|
.replace("Sequence[", "")
|
||||||
|
.replace("Set[", "")[:-1]
|
||||||
|
)
|
||||||
value["list"] = True
|
value["list"] = True
|
||||||
else:
|
else:
|
||||||
value["list"] = False
|
value["list"] = False
|
||||||
|
|
|
||||||
8
src/frontend/package-lock.json
generated
8
src/frontend/package-lock.json
generated
|
|
@ -37,7 +37,7 @@
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"class-variance-authority": "^0.6.0",
|
"class-variance-authority": "^0.6.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"dompurify": "^3.0.3",
|
"dompurify": "^3.0.4",
|
||||||
"esbuild": "^0.17.18",
|
"esbuild": "^0.17.18",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.233.0",
|
"lucide-react": "^0.233.0",
|
||||||
|
|
@ -5142,9 +5142,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dompurify": {
|
"node_modules/dompurify": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.4.tgz",
|
||||||
"integrity": "sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ=="
|
"integrity": "sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ=="
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.440",
|
"version": "1.4.440",
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"class-variance-authority": "^0.6.0",
|
"class-variance-authority": "^0.6.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"dompurify": "^3.0.3",
|
"dompurify": "^3.0.4",
|
||||||
"esbuild": "^0.17.18",
|
"esbuild": "^0.17.18",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.233.0",
|
"lucide-react": "^0.233.0",
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,7 @@ export default function ParameterComponent({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
|
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
style={{
|
style={{
|
||||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
color: nodeColors[item.family] ?? nodeColors.unknown,
|
||||||
|
|
@ -129,7 +128,7 @@ export default function ParameterComponent({
|
||||||
{nodeNames[item.family] ?? ""}{" "}
|
{nodeNames[item.family] ?? ""}{" "}
|
||||||
<span className="text-xs">
|
<span className="text-xs">
|
||||||
{" "}
|
{" "}
|
||||||
{item.type == "" ? '' : ' - '}
|
{item.type == "" ? "" : " - "}
|
||||||
{item.type.split(", ").length > 2
|
{item.type.split(", ").length > 2
|
||||||
? item.type.split(", ").map((el, i) => (
|
? item.type.split(", ").map((el, i) => (
|
||||||
<React.Fragment key={el + i}>
|
<React.Fragment key={el + i}>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export default function ExtraSidebar() {
|
||||||
item.href.split("/")[2] === current[4]
|
item.href.split("/")[2] === current[4]
|
||||||
? "text-ring"
|
? "text-ring"
|
||||||
: "text-ring group-hover:text-accent-foreground",
|
: "text-ring group-hover:text-accent-foreground",
|
||||||
"mr-3 flex-shrink-0 h-6 w-6"
|
"mr-3 h-6 w-6 flex-shrink-0"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{item.name}
|
{item.name}
|
||||||
|
|
@ -71,8 +71,8 @@ export default function ExtraSidebar() {
|
||||||
<span className="flex-1">{item.name}</span>
|
<span className="flex-1">{item.name}</span>
|
||||||
<svg
|
<svg
|
||||||
className={classNames(
|
className={classNames(
|
||||||
open ? "text-ring rotate-90" : "text-ring",
|
open ? "rotate-90 text-ring" : "text-ring",
|
||||||
"ml-3 h-5 w-5 flex-shrink-0 transition-rotate duration-150 ease-in-out group-hover:text-accent-foreground"
|
"transition-rotate ml-3 h-5 w-5 flex-shrink-0 duration-150 ease-in-out group-hover:text-accent-foreground"
|
||||||
)}
|
)}
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ShadTooltipProps } from "../../types/components";
|
import { RadialProgressType, ShadToolTipType } from "../../types/components";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
|
|
@ -6,26 +6,22 @@ import {
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "../ui/tooltip";
|
} from "../ui/tooltip";
|
||||||
|
|
||||||
const ShadTooltip = ({
|
export default function ShadTooltip({
|
||||||
delayDuration = 500,
|
|
||||||
side,
|
|
||||||
content,
|
content,
|
||||||
|
side,
|
||||||
|
asChild = true,
|
||||||
children,
|
children,
|
||||||
style
|
delayDuration,
|
||||||
}: ShadTooltipProps) => {
|
}: ShadToolTipType) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip delayDuration={delayDuration}>
|
<Tooltip delayDuration={delayDuration}>
|
||||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
||||||
|
|
||||||
<TooltipContent
|
<TooltipContent side={side} avoidCollisions={false} sticky="always">
|
||||||
className={style}
|
|
||||||
side={side} avoidCollisions={false} sticky="always">
|
|
||||||
{content}
|
{content}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default ShadTooltip;
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,15 @@ import {
|
||||||
Card,
|
Card,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
} from "../ui/card";
|
} from "../ui/card";
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog";
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "../ui/dialog";
|
||||||
import { Button } from "@mui/material";
|
import { Button } from "@mui/material";
|
||||||
|
|
||||||
export const CardComponent = ({
|
export const CardComponent = ({
|
||||||
|
|
@ -42,24 +50,26 @@ export const CardComponent = ({
|
||||||
</span>
|
</span>
|
||||||
{onDelete && (
|
{onDelete && (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<button className="flex self-start">
|
<button className="flex self-start">
|
||||||
<Trash2 className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
<Trash2 className="h-4 w-4 text-primary opacity-0 transition-all group-hover:opacity-100" />
|
||||||
</button>
|
</button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
|
<DialogTitle>Are you sure absolutely sure?</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
This action cannot be undone. Are you sure you want to permanently
|
This action cannot be undone. Are you sure you want to
|
||||||
delete this file from our servers?
|
permanently delete this file from our servers?
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button type="submit" onClick={onDelete}>Confirm</Button>
|
<Button type="submit" onClick={onDelete}>
|
||||||
</DialogFooter>
|
Confirm
|
||||||
</DialogContent>
|
</Button>
|
||||||
</Dialog>
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="pb-2 pt-2">
|
<CardDescription className="pb-2 pt-2">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { useSSE } from "../../../contexts/SSEContext";
|
||||||
import { typesContext } from "../../../contexts/typesContext";
|
import { typesContext } from "../../../contexts/typesContext";
|
||||||
import { alertContext } from "../../../contexts/alertContext";
|
import { alertContext } from "../../../contexts/alertContext";
|
||||||
import { postBuildInit } from "../../../controllers/API";
|
import { postBuildInit } from "../../../controllers/API";
|
||||||
import ShadTooltip from "../../ShadTooltipComponent";
|
|
||||||
|
|
||||||
import RadialProgressComponent from "../../RadialProgress";
|
import RadialProgressComponent from "../../RadialProgress";
|
||||||
import { TabsContext } from "../../../contexts/tabsContext";
|
import { TabsContext } from "../../../contexts/tabsContext";
|
||||||
|
|
@ -96,10 +95,9 @@ export default function BuildTrigger({
|
||||||
[flowId]: {
|
[flowId]: {
|
||||||
...old[flowId],
|
...old[flowId],
|
||||||
formKeysData: parsedData,
|
formKeysData: parsedData,
|
||||||
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, process the data
|
// Otherwise, process the data
|
||||||
const isValid = processStreamResult(parsedData);
|
const isValid = processStreamResult(parsedData);
|
||||||
|
|
@ -168,7 +166,7 @@ export default function BuildTrigger({
|
||||||
leaveFrom="translate-y-0"
|
leaveFrom="translate-y-0"
|
||||||
leaveTo="translate-y-96"
|
leaveTo="translate-y-96"
|
||||||
>
|
>
|
||||||
<div className="fixed right-4 bottom-20">
|
<div className="fixed bottom-20 right-4">
|
||||||
<div
|
<div
|
||||||
className={`${eventClick} align-center shadow-round-btn-shadow hover:shadow-round-btn-shadow flex h-12 w-12 cursor-pointer justify-center rounded-full bg-border px-3 py-1 shadow-md`}
|
className={`${eventClick} align-center shadow-round-btn-shadow hover:shadow-round-btn-shadow flex h-12 w-12 cursor-pointer justify-center rounded-full bg-border px-3 py-1 shadow-md`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -189,7 +187,10 @@ export default function BuildTrigger({
|
||||||
) : isBuilding ? (
|
) : isBuilding ? (
|
||||||
<Loading strokeWidth={1.5} className="stroke-build-trigger" />
|
<Loading strokeWidth={1.5} className="stroke-build-trigger" />
|
||||||
) : (
|
) : (
|
||||||
<Zap strokeWidth={1.5} className="sh-6 w-6 fill-build-trigger stroke-1 stroke-build-trigger" />
|
<Zap
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="sh-6 w-6 fill-build-trigger stroke-build-trigger stroke-1"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,30 @@ import { MessagesSquare } from "lucide-react";
|
||||||
|
|
||||||
import { alertContext } from "../../../contexts/alertContext";
|
import { alertContext } from "../../../contexts/alertContext";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import ShadTooltip from "../../ShadTooltipComponent";
|
import {
|
||||||
|
CHAT_CANNOT_OPEN_DESCRIPTION,
|
||||||
|
CHAT_CANNOT_OPEN_TITLE,
|
||||||
|
FLOW_NOT_BUILT_DESCRIPTION,
|
||||||
|
FLOW_NOT_BUILT_TITLE,
|
||||||
|
} from "../../../constants";
|
||||||
|
|
||||||
export default function ChatTrigger({ open, setOpen, isBuilt }) {
|
export default function ChatTrigger({ open, setOpen, isBuilt, canOpen }) {
|
||||||
const { setErrorData } = useContext(alertContext);
|
const { setErrorData } = useContext(alertContext);
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
if (isBuilt) {
|
if (isBuilt) {
|
||||||
setOpen(true);
|
if (canOpen) {
|
||||||
|
setOpen(true);
|
||||||
|
} else {
|
||||||
|
setErrorData({
|
||||||
|
title: CHAT_CANNOT_OPEN_TITLE,
|
||||||
|
list: [CHAT_CANNOT_OPEN_DESCRIPTION],
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Flow not built",
|
title: FLOW_NOT_BUILT_TITLE,
|
||||||
list: ["Please build the flow before chatting"],
|
list: [FLOW_NOT_BUILT_DESCRIPTION],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,15 +42,26 @@ export default function ChatTrigger({ open, setOpen, isBuilt }) {
|
||||||
leaveFrom="translate-y-0"
|
leaveFrom="translate-y-0"
|
||||||
leaveTo="translate-y-96"
|
leaveTo="translate-y-96"
|
||||||
>
|
>
|
||||||
<button onClick={handleClick} className={ "transition-all fixed bottom-4 right-4 flex justify-center items-center py-1 px-3 w-12 h-12 rounded-full shadow-md shadow-round-btn-shadow hover:shadow-round-btn-shadow bg-border "+ (!isBuilt ? "cursor-not-allowed" : "cursor-pointer")}>
|
<button
|
||||||
<div className="flex gap-3">
|
onClick={handleClick}
|
||||||
<MessagesSquare
|
className={
|
||||||
className={"h-6 w-6 transition-all " + (isBuilt ? "fill-chat-trigger stroke-chat-trigger stroke-1" : "fill-chat-trigger-disabled stroke-1 stroke-chat-trigger-disabled")}
|
"shadow-round-btn-shadow hover:shadow-round-btn-shadow fixed bottom-4 right-4 flex h-12 w-12 items-center justify-center rounded-full bg-border px-3 py-1 shadow-md transition-all " +
|
||||||
style={{ color: "white" }}
|
(!isBuilt || !canOpen ? "cursor-not-allowed" : "cursor-pointer")
|
||||||
strokeWidth={1.5}
|
}
|
||||||
/>
|
>
|
||||||
</div>
|
<div className="flex gap-3">
|
||||||
</button>
|
<MessagesSquare
|
||||||
|
className={
|
||||||
|
"h-6 w-6 transition-all " +
|
||||||
|
(isBuilt && canOpen
|
||||||
|
? "fill-chat-trigger stroke-chat-trigger stroke-1"
|
||||||
|
: "fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1")
|
||||||
|
}
|
||||||
|
style={{ color: "white" }}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</Transition>
|
</Transition>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import * as _ from "lodash";
|
||||||
export default function Chat({ flow }: ChatType) {
|
export default function Chat({ flow }: ChatType) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [isBuilt, setIsBuilt] = useState(false);
|
const [isBuilt, setIsBuilt] = useState(false);
|
||||||
|
const [canOpen, setCanOpen] = useState(false);
|
||||||
const { tabsState } = useContext(TabsContext);
|
const { tabsState } = useContext(TabsContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -58,6 +59,17 @@ export default function Chat({ flow }: ChatType) {
|
||||||
) {
|
) {
|
||||||
setIsBuilt(false);
|
setIsBuilt(false);
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
tabsState &&
|
||||||
|
tabsState[flow.id] &&
|
||||||
|
tabsState[flow.id].formKeysData &&
|
||||||
|
tabsState[flow.id].formKeysData.input_keys &&
|
||||||
|
Object.keys(tabsState[flow.id].formKeysData.input_keys).length > 0
|
||||||
|
) {
|
||||||
|
setCanOpen(true);
|
||||||
|
} else {
|
||||||
|
setCanOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
prevNodesRef.current = currentNodes;
|
prevNodesRef.current = currentNodes;
|
||||||
}, [tabsState]);
|
}, [tabsState]);
|
||||||
|
|
@ -71,10 +83,15 @@ export default function Chat({ flow }: ChatType) {
|
||||||
setIsBuilt={setIsBuilt}
|
setIsBuilt={setIsBuilt}
|
||||||
isBuilt={isBuilt}
|
isBuilt={isBuilt}
|
||||||
/>
|
/>
|
||||||
{isBuilt && (
|
{isBuilt && canOpen && (
|
||||||
<FormModal key={flow.id} flow={flow} open={open} setOpen={setOpen} />
|
<FormModal key={flow.id} flow={flow} open={open} setOpen={setOpen} />
|
||||||
)}
|
)}
|
||||||
<ChatTrigger open={open} setOpen={setOpen} isBuilt={isBuilt} />
|
<ChatTrigger
|
||||||
|
canOpen={canOpen}
|
||||||
|
open={open}
|
||||||
|
setOpen={setOpen}
|
||||||
|
isBuilt={isBuilt}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { PopUpContext } from "../../contexts/popUpContext";
|
import { PopUpContext } from "../../contexts/popUpContext";
|
||||||
import CodeAreaModal from "../../modals/codeAreaModal/v2";
|
import CodeAreaModal from "../../modals/codeAreaModal/v2";
|
||||||
import TextAreaModal from "../../modals/textAreaModal";
|
import { CodeAreaComponentType } from "../../types/components";
|
||||||
import { CodeAreaComponentType, TextAreaComponentType } from "../../types/components";
|
|
||||||
|
|
||||||
|
|
||||||
import { ExternalLink } from "lucide-react";
|
import { ExternalLink } from "lucide-react";
|
||||||
|
|
||||||
|
|
@ -54,7 +52,8 @@ export default function CodeAreaComponent({
|
||||||
className={
|
className={
|
||||||
editNode
|
editNode
|
||||||
? "input-edit-node input-dialog "
|
? "input-edit-node input-dialog "
|
||||||
: "input-primary input-dialog " + (disabled ? "input-disable" : "")
|
: "input-dialog input-primary " +
|
||||||
|
(disabled ? "input-disable" : "")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{myValue !== "" ? myValue : "Type something..."}
|
{myValue !== "" ? myValue : "Type something..."}
|
||||||
|
|
@ -63,9 +62,9 @@ export default function CodeAreaComponent({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
openPopUp(
|
openPopUp(
|
||||||
<CodeAreaModal
|
<CodeAreaModal
|
||||||
key={myValue}
|
key={myValue}
|
||||||
dynamic={dynamic}
|
dynamic={dynamic}
|
||||||
setNodeClass={setNodeClass}
|
setNodeClass={setNodeClass}
|
||||||
value={myValue}
|
value={myValue}
|
||||||
nodeClass={nodeClass}
|
nodeClass={nodeClass}
|
||||||
setValue={(t: string) => {
|
setValue={(t: string) => {
|
||||||
|
|
@ -77,7 +76,10 @@ export default function CodeAreaComponent({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!editNode && (
|
{!editNode && (
|
||||||
<ExternalLink strokeWidth={1.5} className="w-6 h-6 hover:text-accent-foreground ml-3" />
|
<ExternalLink
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="ml-3 h-6 w-6 hover:text-accent-foreground"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ export default function Dropdown({
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
className={
|
className={
|
||||||
editNode
|
editNode
|
||||||
? "border-1 relative pr-8 input-edit-node"
|
? "border-1 input-edit-node relative pr-8"
|
||||||
: "py-2 pl-3 pr-10 text-left input-primary"
|
: "input-primary py-2 pl-3 pr-10 text-left"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="block w-full truncate bg-background">
|
<span className="block w-full truncate bg-background">
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ export default function Header() {
|
||||||
href="https://github.com/logspace-ai/langflow"
|
href="https://github.com/logspace-ai/langflow"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="inline-flex shadow-sm items-center justify-center text-sm font-medium 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 text-muted-foreground border border-input hover:bg-accent hover:text-accent-foreground h-9 px-3 pr-0 rounded-md"
|
className="inline-flex h-9 items-center justify-center rounded-md border border-input px-3 pr-0 text-sm font-medium text-muted-foreground shadow-sm ring-offset-background hover:bg-accent hover:text-accent-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"
|
||||||
>
|
>
|
||||||
<FaGithub className="mr-2 h-5 w-5" />
|
<FaGithub className="mr-2 h-5 w-5" />
|
||||||
Star
|
Star
|
||||||
|
|
@ -114,7 +114,7 @@ export default function Header() {
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="text-muted-foreground hover:text-accent-foreground relative"
|
className="relative text-muted-foreground hover:text-accent-foreground"
|
||||||
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
||||||
setNotificationCenter(false);
|
setNotificationCenter(false);
|
||||||
const { top, left } = (
|
const { top, left } = (
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,10 @@ export default function InputFileComponent({
|
||||||
</span>
|
</span>
|
||||||
<button onClick={handleButtonClick}>
|
<button onClick={handleButtonClick}>
|
||||||
{!editNode && !loading && (
|
{!editNode && !loading && (
|
||||||
<FileSearch2 strokeWidth={1.5} className="w-6 h-6 hover:text-accent-foreground" />
|
<FileSearch2
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="h-6 w-6 hover:text-accent-foreground"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{!editNode && loading && (
|
{!editNode && loading && (
|
||||||
<span className="loading loading-spinner loading-sm pointer-events-none h-8 pl-3"></span>
|
<span className="loading loading-spinner loading-sm pointer-events-none h-8 pl-3"></span>
|
||||||
|
|
|
||||||
|
|
@ -47,65 +47,66 @@ export default function PromptAreaComponent({
|
||||||
}, [reactFlowInstance, field_name]);
|
}, [reactFlowInstance, field_name]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={disabled ? "cursor-not-allowed" : ""}>
|
||||||
className={
|
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
|
||||||
disabled ? "pointer-events-none w-full cursor-not-allowed" : " w-full"
|
<div className="flex w-full items-center">
|
||||||
}
|
<span
|
||||||
>
|
onClick={() => {
|
||||||
<div className="flex w-full items-center">
|
openPopUp(
|
||||||
<span
|
<GenericModal
|
||||||
onClick={() => {
|
type={TypeModal.PROMPT}
|
||||||
openPopUp(
|
value={myValue}
|
||||||
<GenericModal
|
buttonText="Check & Save"
|
||||||
type={TypeModal.PROMPT}
|
modalTitle="Edit Prompt"
|
||||||
value={myValue}
|
setValue={(t: string) => {
|
||||||
buttonText="Check & Save"
|
setMyValue(t);
|
||||||
modalTitle="Edit Prompt"
|
onChange(t);
|
||||||
setValue={(t: string) => {
|
}}
|
||||||
setMyValue(t);
|
nodeClass={nodeClass}
|
||||||
onChange(t);
|
setNodeClass={setNodeClass}
|
||||||
}}
|
/>
|
||||||
nodeClass={nodeClass}
|
);
|
||||||
setNodeClass={setNodeClass}
|
}}
|
||||||
|
className={
|
||||||
|
editNode
|
||||||
|
? " input-edit-node " + " input-dialog "
|
||||||
|
: (disabled ? " input-disable " : "") +
|
||||||
|
" input-primary " +
|
||||||
|
" input-dialog "
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{myValue !== "" ? myValue : "Type your prompt here"}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
openPopUp(
|
||||||
|
<GenericModal
|
||||||
|
field_name={field_name}
|
||||||
|
type={TypeModal.PROMPT}
|
||||||
|
value={myValue}
|
||||||
|
buttonText="Check & Save"
|
||||||
|
modalTitle="Edit Prompt"
|
||||||
|
setValue={(t: string) => {
|
||||||
|
setMyValue(t);
|
||||||
|
onChange(t);
|
||||||
|
}}
|
||||||
|
nodeClass={nodeClass}
|
||||||
|
setNodeClass={setNodeClass}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!editNode && (
|
||||||
|
<ExternalLink
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className={
|
||||||
|
"ml-3 h-6 w-6" +
|
||||||
|
(disabled ? " text-ring" : " hover:text-accent-foreground")
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
)}
|
||||||
}}
|
</button>
|
||||||
className={
|
</div>
|
||||||
editNode
|
|
||||||
? " input-edit-node " + " input-dialog "
|
|
||||||
: (disabled ? " input-disable " : "") +
|
|
||||||
" input-primary " +
|
|
||||||
" input-dialog "
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{myValue !== "" ? myValue : "Type your prompt here"}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
openPopUp(
|
|
||||||
<GenericModal
|
|
||||||
field_name={field_name}
|
|
||||||
type={TypeModal.PROMPT}
|
|
||||||
value={myValue}
|
|
||||||
buttonText="Check & Save"
|
|
||||||
modalTitle="Edit Prompt"
|
|
||||||
setValue={(t: string) => {
|
|
||||||
setMyValue(t);
|
|
||||||
onChange(t);
|
|
||||||
}}
|
|
||||||
nodeClass={nodeClass}
|
|
||||||
setNodeClass={setNodeClass}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!editNode && (
|
|
||||||
<ExternalLink
|
|
||||||
strokeWidth={1.5}
|
|
||||||
className="ml-3 h-6 w-6 hover:text-accent-foreground"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,11 @@ export default function TextAreaComponent({
|
||||||
}, [closePopUp]);
|
}, [closePopUp]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
<div className={disabled ? "cursor-not-allowed" : ""}>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
editNode
|
(editNode ? "w-full items-center" : "flex w-full items-center") +
|
||||||
? "w-full items-center"
|
(disabled ? " pointer-events-none" : "")
|
||||||
: "w-full flex items-center"
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|
@ -52,12 +51,14 @@ export default function TextAreaComponent({
|
||||||
}}
|
}}
|
||||||
className={
|
className={
|
||||||
editNode
|
editNode
|
||||||
? "input-edit-node input-dialog "
|
? "input-edit-node input-dialog "
|
||||||
: "input-primary input-dialog " + (disabled ? "input-disable" : "")
|
: "input-dialog input-primary " +
|
||||||
|
(disabled ? "input-disable" : "")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{myValue !== "" ? myValue : "Type something..."}
|
{myValue !== "" ? myValue : "Type something..."}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
openPopUp(
|
openPopUp(
|
||||||
|
|
@ -74,7 +75,15 @@ export default function TextAreaComponent({
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!editNode && <ExternalLink strokeWidth={1.5} className="w-6 h-6 hover:text-accent-foreground ml-3" />}
|
{!editNode && (
|
||||||
|
<ExternalLink
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className={
|
||||||
|
"ml-3 h-6 w-6" +
|
||||||
|
(disabled ? " text-ring" : " hover:text-accent-foreground")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const AccordionTrigger = React.forwardRef<
|
||||||
<AccordionPrimitive.Trigger
|
<AccordionPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ const badgeVariants = cva(
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default:
|
||||||
"bg-primary hover:bg-primary/80 border-transparent text-primary-foreground",
|
"bg-primary hover:bg-primary/80 border-transparent text-primary-foreground",
|
||||||
gray:
|
gray: "bg-border hover:bg-border/80 text-secondary-foreground",
|
||||||
"bg-border hover:bg-border/80 text-secondary-foreground",
|
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
|
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
|
||||||
destructive:
|
destructive:
|
||||||
|
|
@ -21,7 +20,7 @@ const badgeVariants = cva(
|
||||||
sm: "h-4 text-xs",
|
sm: "h-4 text-xs",
|
||||||
md: "h-5 text-sm",
|
md: "h-5 text-sm",
|
||||||
lg: "h-6 text-base",
|
lg: "h-6 text-base",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
|
|
@ -35,7 +34,10 @@ export interface BadgeProps
|
||||||
|
|
||||||
function Badge({ className, variant, size, ...props }: BadgeProps) {
|
function Badge({ className, variant, size, ...props }: BadgeProps) {
|
||||||
return (
|
return (
|
||||||
<div className={cn(badgeVariants({ variant, size }), className)} {...props} />
|
<div
|
||||||
|
className={cn(badgeVariants({ variant, size }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed gap-3 z-50 grid w-full rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
|
"fixed z-50 grid w-full gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const Switch = React.forwardRef<
|
||||||
<SwitchPrimitives.Thumb
|
<SwitchPrimitives.Thumb
|
||||||
aria-disabled={props.disabled}
|
aria-disabled={props.disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 aria-disabled:bg-muted/70 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform aria-disabled:bg-muted/70 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</SwitchPrimitives.Root>
|
</SwitchPrimitives.Root>
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,15 @@ export const CODE_PROMPT_DIALOG_SUBTITLE =
|
||||||
export const PROMPT_DIALOG_SUBTITLE =
|
export const PROMPT_DIALOG_SUBTITLE =
|
||||||
"Create your prompt. Prompts can help guide the behavior of a Language Model.";
|
"Create your prompt. Prompts can help guide the behavior of a Language Model.";
|
||||||
|
|
||||||
|
export const CHAT_CANNOT_OPEN_TITLE = "Chat Cannot Open";
|
||||||
|
|
||||||
|
export const CHAT_CANNOT_OPEN_DESCRIPTION = "This is not a chat flow.";
|
||||||
|
|
||||||
|
export const FLOW_NOT_BUILT_TITLE = "Flow not built";
|
||||||
|
|
||||||
|
export const FLOW_NOT_BUILT_DESCRIPTION =
|
||||||
|
"Please build the flow before chatting.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base text for subtitle of Text Dialog
|
* The base text for subtitle of Text Dialog
|
||||||
* @constant
|
* @constant
|
||||||
|
|
@ -180,7 +189,7 @@ export const EXPORT_CODE_DIALOG =
|
||||||
export const COLUMN_DIV_STYLE =
|
export const COLUMN_DIV_STYLE =
|
||||||
" w-full h-full flex overflow-auto flex-col bg-muted px-16 ";
|
" w-full h-full flex overflow-auto flex-col bg-muted px-16 ";
|
||||||
|
|
||||||
export const NAV_DISPLAY_STYLE =
|
export const NAV_DISPLAY_STYLE =
|
||||||
" w-full flex justify-between py-12 pb-2 px-6 ";
|
" w-full flex justify-between py-12 pb-2 px-6 ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -210,7 +219,8 @@ export const DESCRIPTIONS: string[] = [
|
||||||
"Conversational Cartography Unlocked.",
|
"Conversational Cartography Unlocked.",
|
||||||
"Design, Develop, Dialogize.",
|
"Design, Develop, Dialogize.",
|
||||||
];
|
];
|
||||||
export const BUTTON_DIV_STYLE = " flex gap-2 focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
|
export const BUTTON_DIV_STYLE =
|
||||||
|
" flex gap-2 focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base text for subtitle of code dialog
|
* The base text for subtitle of code dialog
|
||||||
|
|
@ -513,3 +523,10 @@ export const NOUNS: string[] = [
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const USER_PROJECTS_HEADER = "My Collection";
|
export const USER_PROJECTS_HEADER = "My Collection";
|
||||||
|
/**
|
||||||
|
* CSS for highlight HTML
|
||||||
|
* @constant
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const HIGHLIGH_CSS =
|
||||||
|
"block pl-3 pr-14 py-2 w-full h-full text-sm outline-0 border-0 break-all";
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
// create a link element and set its properties
|
// create a link element and set its properties
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = jsonString;
|
link.href = jsonString;
|
||||||
link.download = `${flowName && flowName != "" ? flowName : flows.find((f) => f.id === tabId).name}.json`;
|
link.download = `${
|
||||||
|
flowName && flowName != ""
|
||||||
|
? flowName
|
||||||
|
: flows.find((f) => f.id === tabId).name
|
||||||
|
}.json`;
|
||||||
|
|
||||||
// simulate a click on the link element to trigger the download
|
// simulate a click on the link element to trigger the download
|
||||||
link.click();
|
link.click();
|
||||||
|
|
|
||||||
|
|
@ -104,11 +104,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (closeEdit !== "") {
|
if (closeEdit !== "") {
|
||||||
tweak.current = getTweak;
|
tweak.current = getTweak;
|
||||||
if(tweak.current.length > 0){
|
if (tweak.current.length > 0) {
|
||||||
setActiveTab("3");
|
setActiveTab("3");
|
||||||
openAccordions();
|
openAccordions();
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
startTweaks();
|
startTweaks();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -250,25 +249,25 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
|
|
||||||
function openAccordions() {
|
function openAccordions() {
|
||||||
let accordionsToOpen = [];
|
let accordionsToOpen = [];
|
||||||
tweak.current.forEach((el) => {
|
tweak.current.forEach((el) => {
|
||||||
Object.keys(el).forEach((key) => {
|
Object.keys(el).forEach((key) => {
|
||||||
if (Object.keys(el[key]).length > 0) {
|
if (Object.keys(el[key]).length > 0) {
|
||||||
accordionsToOpen.push(key);
|
accordionsToOpen.push(key);
|
||||||
setOpenAccordion(accordionsToOpen);
|
setOpenAccordion(accordionsToOpen);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onOpenChange={setModalOpen}>
|
<Dialog open={true} onOpenChange={setModalOpen}>
|
||||||
<DialogTrigger></DialogTrigger>
|
<DialogTrigger></DialogTrigger>
|
||||||
<DialogContent className="lg:max-w-[80vw] h-[80vh]">
|
<DialogContent className="h-[80vh] lg:max-w-[80vw]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center">
|
<DialogTitle className="flex items-center">
|
||||||
<span className="pr-2">Code</span>
|
<span className="pr-2">Code</span>
|
||||||
<Code2
|
<Code2
|
||||||
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
className="h-6 w-6 pl-1 text-gray-800 dark:text-white"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
@ -277,7 +276,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
className="w-full h-full overflow-hidden text-center bg-muted rounded-md border"
|
className="h-full w-full overflow-hidden rounded-md border bg-muted text-center"
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
setActiveTab(value);
|
setActiveTab(value);
|
||||||
if (value === "3") {
|
if (value === "3") {
|
||||||
|
|
@ -296,7 +295,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
{Number(activeTab) < 3 && (
|
{Number(activeTab) < 3 && (
|
||||||
<div className="float-right">
|
<div className="float-right">
|
||||||
<button
|
<button
|
||||||
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
|
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
|
||||||
onClick={copyToClipboard}
|
onClick={copyToClipboard}
|
||||||
>
|
>
|
||||||
{isCopied ? <Check size={18} /> : <Clipboard size={15} />}
|
{isCopied ? <Check size={18} /> : <Clipboard size={15} />}
|
||||||
|
|
@ -309,12 +308,12 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<TabsContent
|
<TabsContent
|
||||||
value={index.toString()}
|
value={index.toString()}
|
||||||
className="overflow-hidden w-full h-full px-4 pb-4 -mt-1"
|
className="-mt-1 h-full w-full overflow-hidden px-4 pb-4"
|
||||||
key={index} // Remember to add a unique key prop
|
key={index} // Remember to add a unique key prop
|
||||||
>
|
>
|
||||||
{index < 3 ? (
|
{index < 3 ? (
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
className="w-full overflow-auto h-[60vh]"
|
className="h-[60vh] w-full overflow-auto"
|
||||||
language={tab.mode}
|
language={tab.mode}
|
||||||
style={oneDark}
|
style={oneDark}
|
||||||
>
|
>
|
||||||
|
|
@ -322,10 +321,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
) : index === 3 ? (
|
) : index === 3 ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex w-full h-full mt-2">
|
<div className="mt-2 flex h-full w-full">
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"w-full rounded-lg bg-muted h-[60vh]",
|
"h-[60vh] w-full rounded-lg bg-muted",
|
||||||
1 == 1
|
1 == 1
|
||||||
? "overflow-scroll overflow-x-hidden custom-scroll"
|
? "overflow-scroll overflow-x-hidden custom-scroll"
|
||||||
: "overflow-hidden"
|
: "overflow-hidden"
|
||||||
|
|
@ -338,14 +337,14 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
trigger={t["data"]["id"]}
|
trigger={t["data"]["id"]}
|
||||||
open={openAccordion}
|
open={openAccordion}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-5 h-fit">
|
<div className="flex h-fit flex-col gap-5">
|
||||||
<Table className="table-fixed bg-muted outline-1">
|
<Table className="table-fixed bg-muted outline-1">
|
||||||
<TableHeader className="border-input text-xs font-medium text-ring h-10">
|
<TableHeader className="h-10 border-input text-xs font-medium text-ring">
|
||||||
<TableRow className="dark:border-b-muted">
|
<TableRow className="dark:border-b-muted">
|
||||||
<TableHead className="h-7 text-center">
|
<TableHead className="h-7 text-center">
|
||||||
PARAM
|
PARAM
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="p-0 h-7 text-center">
|
<TableHead className="h-7 p-0 text-center">
|
||||||
VALUE
|
VALUE
|
||||||
</TableHead>
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
@ -379,11 +378,11 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
||||||
key={i}
|
key={i}
|
||||||
className="h-10 dark:border-b-muted"
|
className="h-10 dark:border-b-muted"
|
||||||
>
|
>
|
||||||
<TableCell className="p-0 text-center text-gray-900 text-sm">
|
<TableCell className="p-0 text-center text-sm text-gray-900">
|
||||||
{n}
|
{n}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="p-0 text-center text-gray-900 text-xs dark:text-gray-300">
|
<TableCell className="p-0 text-center text-xs text-gray-900 dark:text-gray-300">
|
||||||
<div className="w-[250px] m-auto">
|
<div className="m-auto w-[250px]">
|
||||||
{t.data.node.template[n]
|
{t.data.node.template[n]
|
||||||
.type === "str" &&
|
.type === "str" &&
|
||||||
!t.data.node.template[n]
|
!t.data.node.template[n]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import "ace-builds/src-noconflict/mode-python";
|
||||||
import "ace-builds/src-noconflict/theme-github";
|
import "ace-builds/src-noconflict/theme-github";
|
||||||
import "ace-builds/src-noconflict/theme-twilight";
|
import "ace-builds/src-noconflict/theme-twilight";
|
||||||
import "ace-builds/src-noconflict/ext-language_tools";
|
import "ace-builds/src-noconflict/ext-language_tools";
|
||||||
import 'ace-builds/src-noconflict/ace';
|
import "ace-builds/src-noconflict/ace";
|
||||||
// import "ace-builds/webpack-resolver";
|
// import "ace-builds/webpack-resolver";
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
import { darkContext } from "../../contexts/darkContext";
|
||||||
import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
import { postCustomComponent, postValidateCode } from "../../controllers/API";
|
||||||
|
|
@ -29,14 +29,13 @@ import {
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "../../components/ui/tabs";
|
} from "../../components/ui/tabs";
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export default function CodeAreaModal({
|
export default function CodeAreaModal({
|
||||||
value,
|
value,
|
||||||
setValue,
|
setValue,
|
||||||
nodeClass,
|
nodeClass,
|
||||||
setNodeClass,
|
setNodeClass,
|
||||||
dynamic
|
dynamic,
|
||||||
}: {
|
}: {
|
||||||
setValue: (value: string) => void;
|
setValue: (value: string) => void;
|
||||||
value: string;
|
value: string;
|
||||||
|
|
@ -50,7 +49,9 @@ export default function CodeAreaModal({
|
||||||
const { dark } = useContext(darkContext);
|
const { dark } = useContext(darkContext);
|
||||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||||
const [activeTab, setActiveTab] = useState("0");
|
const [activeTab, setActiveTab] = useState("0");
|
||||||
const [error, setError] = useState<{ detail: { error: string, traceback: string } }>(null)
|
const [error, setError] = useState<{
|
||||||
|
detail: { error: string; traceback: string };
|
||||||
|
}>(null);
|
||||||
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
function setModalOpen(x: boolean) {
|
function setModalOpen(x: boolean) {
|
||||||
|
|
@ -64,9 +65,9 @@ export default function CodeAreaModal({
|
||||||
}
|
}
|
||||||
console.log(dynamic);
|
console.log(dynamic);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(() => {
|
||||||
setValue(code);
|
setValue(code);
|
||||||
},[code,setValue])
|
}, [code, setValue]);
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -108,39 +109,40 @@ export default function CodeAreaModal({
|
||||||
title: "There is something wrong with this code, please review it",
|
title: "There is something wrong with this code, please review it",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
postCustomComponent(code, nodeClass)
|
||||||
postCustomComponent(code, nodeClass).then((apiReturn) => {
|
.then((apiReturn) => {
|
||||||
const { data } = apiReturn;
|
const { data } = apiReturn;
|
||||||
if (data) {
|
if (data) {
|
||||||
setNodeClass(data);
|
setNodeClass(data);
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
})
|
||||||
setErrorData({
|
.catch((err) => {
|
||||||
title: "There is something wrong with this code, please see the error on the errors tab",
|
setErrorData({
|
||||||
|
title:
|
||||||
|
"There is something wrong with this code, please see the error on the errors tab",
|
||||||
|
});
|
||||||
|
console.log(err.response.data);
|
||||||
|
setError(err.response.data);
|
||||||
});
|
});
|
||||||
console.log(err.response.data);
|
|
||||||
setError(err.response.data);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// axios.get("/api/v1/custom_component_error").catch((err) => {
|
// axios.get("/api/v1/custom_component_error").catch((err) => {
|
||||||
|
|
||||||
// })
|
// })
|
||||||
|
|
||||||
}
|
}
|
||||||
const tabs = [{ name: "code" }, { name: "errors" }]
|
const tabs = [{ name: "code" }, { name: "errors" }];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onOpenChange={setModalOpen}>
|
<Dialog open={true} onOpenChange={setModalOpen}>
|
||||||
<DialogTrigger></DialogTrigger>
|
<DialogTrigger></DialogTrigger>
|
||||||
<DialogContent className="h-[500px] lg:max-w-[700px]">
|
<DialogContent className="min-w-[80vw]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center">
|
<DialogTitle className="flex items-center">
|
||||||
<span className="pr-2">Edit Code</span>
|
<span className="pr-2">Edit Code</span>
|
||||||
<TerminalSquare
|
<TerminalSquare
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
className="h-6 w-6 text-primary pl-1 "
|
className="h-6 w-6 pl-1 text-primary "
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
@ -148,44 +150,65 @@ export default function CodeAreaModal({
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue={"0"}
|
defaultValue={"0"}
|
||||||
className="w-full h-full overflow-hidden text-center bg-muted rounded-md border"
|
className="h-full w-full overflow-hidden rounded-md border bg-muted text-center"
|
||||||
onValueChange={(value) => setActiveTab(value)}
|
onValueChange={(value) => setActiveTab(value)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-start h-72 px-2">
|
<div className="flex h-72 flex-col items-start px-2">
|
||||||
<TabsList>
|
<TabsList>
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<TabsTrigger disabled={index === 1 && error?.detail.error === undefined} key={index} value={index.toString()}>
|
<TabsTrigger
|
||||||
<span className={error?.detail.error !== undefined && index===1 ? "text-destructive" : ""}>{tab.name}</span></TabsTrigger>
|
disabled={index === 1 && error?.detail.error === undefined}
|
||||||
|
key={index}
|
||||||
|
value={index.toString()}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
error?.detail.error !== undefined && index === 1
|
||||||
|
? "text-destructive"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{tab.name}
|
||||||
|
</span>
|
||||||
|
</TabsTrigger>
|
||||||
))}
|
))}
|
||||||
</TabsList>
|
</TabsList>
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<TabsContent
|
<TabsContent
|
||||||
value={index.toString()}
|
value={index.toString()}
|
||||||
className="overflow-hidden w-full h-full px-4 pb-4 mt-1"
|
className="mt-1 h-full w-full overflow-hidden px-4 pb-4"
|
||||||
>
|
>
|
||||||
{tab.name === "code" ? <div className="h-full w-full">
|
{tab.name === "code" ? (
|
||||||
<AceEditor
|
<div className="h-full w-full">
|
||||||
value={code}
|
<AceEditor
|
||||||
mode="python"
|
value={code}
|
||||||
highlightActiveLine={true}
|
mode="python"
|
||||||
showPrintMargin={false}
|
highlightActiveLine={true}
|
||||||
fontSize={14}
|
showPrintMargin={false}
|
||||||
showGutter
|
fontSize={14}
|
||||||
enableLiveAutocompletion
|
showGutter
|
||||||
theme={dark ? "twilight" : "github"}
|
enableLiveAutocompletion
|
||||||
name="CodeEditor"
|
theme={dark ? "twilight" : "github"}
|
||||||
onChange={(value) => {
|
name="CodeEditor"
|
||||||
setCode(value);
|
onChange={(value) => {
|
||||||
}}
|
setCode(value);
|
||||||
className="w-full rounded-lg h-full custom-scroll border-[1px] border-gray-300 dark:border-gray-600"
|
}}
|
||||||
/>
|
className="h-full w-full rounded-lg border-[1px] border-gray-300 custom-scroll dark:border-gray-600"
|
||||||
</div> : <div className="w-full h-full bg-red-200 p-2 flex flex-col overflow-scroll custom-scroll text-left">
|
/>
|
||||||
<h1 className="text-red-600 text-lg">{error?.detail?.error}</h1>
|
</div>
|
||||||
<span className="border border-red-300 w-full"></span>
|
) : (
|
||||||
<div className="text-red-500 text-sm">{error?.detail?.traceback}</div>
|
<div className="flex h-full w-full flex-col overflow-scroll bg-red-200 p-2 text-left custom-scroll">
|
||||||
</div>}
|
<h1 className="text-lg text-red-600">
|
||||||
</TabsContent>))
|
{error?.detail?.error}
|
||||||
}
|
</h1>
|
||||||
|
<span className="w-full border border-red-300"></span>
|
||||||
|
<div className="text-sm text-red-500">
|
||||||
|
{error?.detail?.traceback}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ export default function ExportModal() {
|
||||||
<DialogTitle className="flex items-center">
|
<DialogTitle className="flex items-center">
|
||||||
<span className="pr-2">Export</span>
|
<span className="pr-2">Export</span>
|
||||||
<Download
|
<Download
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
className="h-6 w-6 text-foreground pl-1"
|
className="h-6 w-6 pl-1 text-foreground"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
|
||||||
|
|
@ -52,20 +52,27 @@ export default function ChatInput({
|
||||||
lockChat
|
lockChat
|
||||||
? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
|
? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
|
||||||
: " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
|
: " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
|
||||||
"p-4 form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-16 sm:text-sm"
|
"form-input block w-full rounded-md border-gray-300 p-4 pr-16 custom-scroll dark:border-gray-600 sm:text-sm"
|
||||||
)}
|
)}
|
||||||
placeholder={"Send a message..."}
|
placeholder={"Send a message..."}
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-2 right-4">
|
<div className="absolute bottom-2 right-4">
|
||||||
<button className={classNames("p-2 px-1 transition-all duration-300 rounded-md",chatValue == "" ? "text-primary" : " bg-indigo-600 text-background")} disabled={lockChat} onClick={() => sendMessage()}>
|
<button
|
||||||
|
className={classNames(
|
||||||
|
"rounded-md p-2 px-1 transition-all duration-300",
|
||||||
|
chatValue == "" ? "text-primary" : " bg-indigo-600 text-background"
|
||||||
|
)}
|
||||||
|
disabled={lockChat}
|
||||||
|
onClick={() => sendMessage()}
|
||||||
|
>
|
||||||
{lockChat ? (
|
{lockChat ? (
|
||||||
<Lock
|
<Lock
|
||||||
className="h-5 w-5 ml-1 mr-1 animate-pulse"
|
className="ml-1 mr-1 h-5 w-5 animate-pulse"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LucideSend
|
<LucideSend
|
||||||
className="h-5 w-5 mr-2 rotate-[44deg] "
|
className="mr-2 h-5 w-5 rotate-[44deg] "
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,13 @@ import remarkMath from "remark-math";
|
||||||
import { CodeBlock } from "./codeBlock";
|
import { CodeBlock } from "./codeBlock";
|
||||||
import Convert from "ansi-to-html";
|
import Convert from "ansi-to-html";
|
||||||
import { User2, MessageSquare } from "lucide-react";
|
import { User2, MessageSquare } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "../../../components/ui/accordion";
|
||||||
|
import { Badge } from "../../../components/ui/badge";
|
||||||
|
|
||||||
export default function ChatMessage({
|
export default function ChatMessage({
|
||||||
chat,
|
chat,
|
||||||
|
|
@ -22,145 +29,173 @@ export default function ChatMessage({
|
||||||
lastMessage: boolean;
|
lastMessage: boolean;
|
||||||
}) {
|
}) {
|
||||||
const convert = new Convert({ newline: true });
|
const convert = new Convert({ newline: true });
|
||||||
const [message, setMessage] = useState("");
|
|
||||||
const imgRef = useRef(null);
|
|
||||||
useEffect(() => {
|
|
||||||
setMessage(chat.message);
|
|
||||||
}, [chat.message]);
|
|
||||||
const [hidden, setHidden] = useState(true);
|
const [hidden, setHidden] = useState(true);
|
||||||
|
const [template, setTemplate] = useState(chat.template);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"w-full py-2 px-2 pl-4 pr-9 flex",
|
"flex w-full px-2 py-6 pl-4 pr-9",
|
||||||
chat.isSend
|
chat.isSend ? " bg-border" : " "
|
||||||
? " bg-border"
|
)}
|
||||||
: " "
|
>
|
||||||
)}
|
<div className={classNames("mb-3 ml-3 mr-6 mt-1 ")}>
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"rounded-full overflow-hidden w-8 h-8 flex items-center my-3 mx-3 justify-center"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{!chat.isSend && (
|
|
||||||
<div className="relative w-8 h-8">
|
|
||||||
<img
|
|
||||||
className={
|
|
||||||
"absolute transition-opacity duration-500 scale-150 " +
|
|
||||||
(lockChat ? "opacity-100" : "opacity-0")
|
|
||||||
}
|
|
||||||
src={lastMessage ? AiIcon : AiIconStill}
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
className={
|
|
||||||
"absolute transition-opacity duration-500 scale-150 " +
|
|
||||||
(lockChat ? "opacity-0" : "opacity-100")
|
|
||||||
}
|
|
||||||
src={AiIconStill}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{chat.isSend && (
|
|
||||||
<User2 className="w-6 h-6 text-gray-800 dark:text-gray-200" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!chat.isSend ? (
|
{!chat.isSend ? (
|
||||||
<div className="w-full text-start flex items-center">
|
<div className="flex h-8 w-8 items-center justify-center overflow-hidden rounded-md bg-[#afe6ef] p-5 text-2xl ">
|
||||||
<div className="w-full relative text-start inline-block text-primary text-sm font-normal">
|
🤖
|
||||||
{hidden && chat.thought && chat.thought !== "" && (
|
|
||||||
<div
|
|
||||||
onClick={() => setHidden((prev) => !prev)}
|
|
||||||
className="absolute -top-1 -left-2 cursor-pointer"
|
|
||||||
>
|
|
||||||
<MessageSquare className="w-5 h-5 animate-bounce dark:text-white" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{chat.thought && chat.thought !== "" && !hidden && (
|
|
||||||
<div
|
|
||||||
onClick={() => setHidden((prev) => !prev)}
|
|
||||||
className=" text-start inline-block rounded-md text-primary h-full border border-gray-300 dark:border-gray-500
|
|
||||||
bg-muted dark:bg-gray-800 w-[95%] pb-3 pt-3 px-2 ml-3 cursor-pointer scrollbar-hide overflow-scroll"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: convert.toHtml(chat.thought),
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
)}
|
|
||||||
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
|
||||||
<div className="w-full pb-3 pt-3">
|
|
||||||
<div className="dark:text-white w-full">
|
|
||||||
<div className="w-full">
|
|
||||||
<ReactMarkdown
|
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
|
||||||
rehypePlugins={[rehypeMathjax]}
|
|
||||||
className="markdown prose dark:prose-invert text-primary max-w-full"
|
|
||||||
components={{
|
|
||||||
code({ node, inline, className, children, ...props }) {
|
|
||||||
if (children.length) {
|
|
||||||
if (children[0] == "▍") {
|
|
||||||
return (
|
|
||||||
<span className="animate-pulse cursor-default mt-1">
|
|
||||||
▍
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
children[0] = (children[0] as string).replace(
|
|
||||||
"`▍`",
|
|
||||||
"▍"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const match = /language-(\w+)/.exec(className || "");
|
|
||||||
|
|
||||||
return !inline ? (
|
|
||||||
<CodeBlock
|
|
||||||
key={Math.random()}
|
|
||||||
language={(match && match[1]) || ""}
|
|
||||||
value={String(children).replace(/\n$/, "")}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<code className={className} {...props}>
|
|
||||||
{children}
|
|
||||||
</code>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{message}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
{chat.files && (
|
|
||||||
<div className="my-2 w-full">
|
|
||||||
{chat.files.map((file, index) => {
|
|
||||||
return (
|
|
||||||
<div key={index} className="my-2 w-full">
|
|
||||||
<FileCard
|
|
||||||
fileName={"Generated File"}
|
|
||||||
fileType={file.data_type}
|
|
||||||
content={file.data}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full flex items-center">
|
<div className="flex h-8 w-8 items-center justify-center overflow-hidden rounded-md bg-[#aface9] p-5 text-2xl ">
|
||||||
<div className="text-start inline-block">
|
👨💻
|
||||||
<span
|
|
||||||
className="text-primary"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: message.replace(/\n/g, "<br>"),
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{!chat.isSend ? (
|
||||||
|
<div className="flex w-full items-center text-start">
|
||||||
|
<div className="relative inline-block w-full text-start text-sm font-normal text-primary">
|
||||||
|
{hidden && chat.thought && chat.thought !== "" && (
|
||||||
|
<div
|
||||||
|
onClick={() => setHidden((prev) => !prev)}
|
||||||
|
className="absolute -left-2 -top-1 cursor-pointer"
|
||||||
|
>
|
||||||
|
<MessageSquare className="h-5 w-5 animate-bounce dark:text-white" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{chat.thought && chat.thought !== "" && !hidden && (
|
||||||
|
<div
|
||||||
|
onClick={() => setHidden((prev) => !prev)}
|
||||||
|
className=" ml-3 inline-block h-full w-[95%] cursor-pointer overflow-scroll rounded-md border
|
||||||
|
border-gray-300 bg-muted px-2 text-start text-primary scrollbar-hide dark:border-gray-500 dark:bg-gray-800"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: convert.toHtml(chat.thought),
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="w-full dark:text-white">
|
||||||
|
<div className="w-full">
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
|
rehypePlugins={[rehypeMathjax]}
|
||||||
|
className="markdown prose max-w-full text-primary dark:prose-invert"
|
||||||
|
components={{
|
||||||
|
code({ node, inline, className, children, ...props }) {
|
||||||
|
if (children.length) {
|
||||||
|
if (children[0] == "▍") {
|
||||||
|
return (
|
||||||
|
<span className="mt-1 animate-pulse cursor-default">
|
||||||
|
▍
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
children[0] = (children[0] as string).replace(
|
||||||
|
"`▍`",
|
||||||
|
"▍"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = /language-(\w+)/.exec(className || "");
|
||||||
|
|
||||||
|
return !inline ? (
|
||||||
|
<CodeBlock
|
||||||
|
key={Math.random()}
|
||||||
|
language={(match && match[1]) || ""}
|
||||||
|
value={String(children).replace(/\n$/, "")}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<code className={className} {...props}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{chat.message.toString()}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
{chat.files && (
|
||||||
|
<div className="my-2 w-full">
|
||||||
|
{chat.files.map((file, index) => {
|
||||||
|
return (
|
||||||
|
<div key={index} className="my-2 w-full">
|
||||||
|
<FileCard
|
||||||
|
fileName={"Generated File"}
|
||||||
|
fileType={file.data_type}
|
||||||
|
content={file.data}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex w-full items-center">
|
||||||
|
<div className="inline-block text-start">
|
||||||
|
<span className=" break-all text-primary">
|
||||||
|
<Accordion type="single" collapsible className="mb-4">
|
||||||
|
<AccordionItem
|
||||||
|
className=" rounded-md border border-ring/60 bg-muted px-4"
|
||||||
|
value="prompt"
|
||||||
|
>
|
||||||
|
<AccordionTrigger className="flex gap-4 text-base font-semibold">
|
||||||
|
Initial Prompt
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="max-h-96 overflow-auto break-all p-2 text-base">
|
||||||
|
{
|
||||||
|
// Make all the variables that are inside curly braces bold
|
||||||
|
template.split("\n").map((line, index) => {
|
||||||
|
const regex = /{([^}]+)}/g;
|
||||||
|
let match;
|
||||||
|
let parts = [];
|
||||||
|
let lastIndex = 0;
|
||||||
|
while ((match = regex.exec(line)) !== null) {
|
||||||
|
// Push text up to the match
|
||||||
|
if (match.index !== lastIndex) {
|
||||||
|
parts.push(line.substring(lastIndex, match.index));
|
||||||
|
}
|
||||||
|
// Push div with matched text
|
||||||
|
parts.push(
|
||||||
|
<div>
|
||||||
|
<div className="my-1 inline-block rounded-md bg-indigo-100 px-2">
|
||||||
|
<span key={match.index}>
|
||||||
|
<span className=" text-xs font-semibold text-high-indigo">
|
||||||
|
{match[1]}
|
||||||
|
</span>
|
||||||
|
{chat.message[match[1]] ? (
|
||||||
|
<span>
|
||||||
|
{chat.message[match[1]]}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
// Update last index
|
||||||
|
lastIndex = regex.lastIndex;
|
||||||
|
}
|
||||||
|
// Push text after the last match
|
||||||
|
if (lastIndex !== line.length) {
|
||||||
|
parts.push(line.substring(lastIndex));
|
||||||
|
}
|
||||||
|
return <p>{parts}</p>;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
{chat.message[chat.chatKey]}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,19 +91,23 @@ export default function FormModal({
|
||||||
var isStream = false;
|
var isStream = false;
|
||||||
|
|
||||||
const addChatHistory = (
|
const addChatHistory = (
|
||||||
message: string,
|
message: string | Object,
|
||||||
isSend: boolean,
|
isSend: boolean,
|
||||||
|
chatKey: string,
|
||||||
|
template?: string,
|
||||||
thought?: string,
|
thought?: string,
|
||||||
files?: Array<any>
|
files?: Array<any>
|
||||||
) => {
|
) => {
|
||||||
setChatHistory((old) => {
|
setChatHistory((old) => {
|
||||||
let newChat = _.cloneDeep(old);
|
let newChat = _.cloneDeep(old);
|
||||||
if (files) {
|
if (files) {
|
||||||
newChat.push({ message, isSend, files, thought });
|
newChat.push({ message, isSend, files, thought, chatKey });
|
||||||
} else if (thought) {
|
} else if (thought) {
|
||||||
newChat.push({ message, isSend, thought });
|
newChat.push({ message, isSend, thought, chatKey });
|
||||||
|
} else if (template) {
|
||||||
|
newChat.push({ message, isSend, chatKey, template });
|
||||||
} else {
|
} else {
|
||||||
newChat.push({ message, isSend });
|
newChat.push({ message, isSend, chatKey });
|
||||||
}
|
}
|
||||||
return newChat;
|
return newChat;
|
||||||
});
|
});
|
||||||
|
|
@ -174,7 +178,9 @@ export default function FormModal({
|
||||||
intermediate_steps?: string;
|
intermediate_steps?: string;
|
||||||
is_bot: boolean;
|
is_bot: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
|
template: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
chatKey: string;
|
||||||
files?: Array<any>;
|
files?: Array<any>;
|
||||||
}) => {
|
}) => {
|
||||||
if (chatItem.message) {
|
if (chatItem.message) {
|
||||||
|
|
@ -182,14 +188,18 @@ export default function FormModal({
|
||||||
chatItem.files
|
chatItem.files
|
||||||
? {
|
? {
|
||||||
isSend: !chatItem.is_bot,
|
isSend: !chatItem.is_bot,
|
||||||
message: formatMessage(chatItem.message),
|
message: chatItem.message,
|
||||||
|
template: chatItem.template,
|
||||||
thought: chatItem.intermediate_steps,
|
thought: chatItem.intermediate_steps,
|
||||||
files: chatItem.files,
|
files: chatItem.files,
|
||||||
|
chatKey: chatItem.chatKey,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
isSend: !chatItem.is_bot,
|
isSend: !chatItem.is_bot,
|
||||||
message: formatMessage(chatItem.message),
|
message: chatItem.message,
|
||||||
|
template: chatItem.template,
|
||||||
thought: chatItem.intermediate_steps,
|
thought: chatItem.intermediate_steps,
|
||||||
|
chatKey: chatItem.chatKey,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -199,7 +209,7 @@ export default function FormModal({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (data.type === "start") {
|
if (data.type === "start") {
|
||||||
addChatHistory("", false);
|
addChatHistory("", false, chatKey);
|
||||||
isStream = true;
|
isStream = true;
|
||||||
}
|
}
|
||||||
if (data.type === "end") {
|
if (data.type === "end") {
|
||||||
|
|
@ -319,23 +329,6 @@ export default function FormModal({
|
||||||
ref.current.focus();
|
ref.current.focus();
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
function formatMessage(inputs: any): string {
|
|
||||||
if (inputs) {
|
|
||||||
if (typeof inputs == "string") return inputs;
|
|
||||||
// inputs is a object with the keys and values being input_keys and keysValue
|
|
||||||
// so the formated message is a string with the keys and values separated by ": "
|
|
||||||
let message = "";
|
|
||||||
for (const [key, value] of Object.entries(inputs)) {
|
|
||||||
// make key bold
|
|
||||||
// dangerouslySetInnerHTML={{
|
|
||||||
// __html: message.replace(/\n/g, "<br>"),
|
|
||||||
// }}
|
|
||||||
message += `<b>${key}</b>: ${value}\n`;
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
if (chatValue !== "") {
|
if (chatValue !== "") {
|
||||||
|
|
@ -344,8 +337,13 @@ export default function FormModal({
|
||||||
setLockChat(true);
|
setLockChat(true);
|
||||||
let inputs = tabsState[id.current].formKeysData.input_keys;
|
let inputs = tabsState[id.current].formKeysData.input_keys;
|
||||||
setChatValue("");
|
setChatValue("");
|
||||||
const message = formatMessage(inputs);
|
const message = inputs;
|
||||||
addChatHistory(message, true);
|
addChatHistory(
|
||||||
|
message,
|
||||||
|
true,
|
||||||
|
chatKey,
|
||||||
|
tabsState[flow.id].formKeysData.template
|
||||||
|
);
|
||||||
sendAll({
|
sendAll({
|
||||||
...reactFlowInstance.toObject(),
|
...reactFlowInstance.toObject(),
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
|
|
@ -404,7 +402,7 @@ export default function FormModal({
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="mt-2 flex h-[80vh] w-full ">
|
<div className="mt-2 flex h-[80vh] w-full ">
|
||||||
<div className="mr-6 flex h-full w-2/5 flex-col justify-start overflow-auto scrollbar-hide">
|
<div className="mr-6 flex h-full w-2/6 flex-col justify-start overflow-auto scrollbar-hide">
|
||||||
<div className="flex items-center py-2">
|
<div className="flex items-center py-2">
|
||||||
<Variable className=" -ml-px mr-1 h-4 w-4 text-primary"></Variable>
|
<Variable className=" -ml-px mr-1 h-4 w-4 text-primary"></Variable>
|
||||||
<span className="text-md font-semibold text-primary">
|
<span className="text-md font-semibold text-primary">
|
||||||
|
|
@ -496,7 +494,7 @@ export default function FormModal({
|
||||||
))}
|
))}
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="flex w-full flex-1 flex-col">
|
||||||
<div className="relative flex h-full w-full flex-col rounded-md border bg-muted">
|
<div className="relative flex h-full w-full flex-col rounded-md border bg-muted">
|
||||||
<div className="absolute right-3 top-3 z-50">
|
<div className="absolute right-3 top-3 z-50">
|
||||||
<button disabled={lockChat} onClick={() => clearChat()}>
|
<button disabled={lockChat} onClick={() => clearChat()}>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useContext, useRef, useState } from "react";
|
import { useContext, useRef, useState, useEffect } from "react";
|
||||||
import { PopUpContext } from "../../contexts/popUpContext";
|
import { PopUpContext } from "../../contexts/popUpContext";
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
import { darkContext } from "../../contexts/darkContext";
|
||||||
import { postValidatePrompt } from "../../controllers/API";
|
import { postValidatePrompt } from "../../controllers/API";
|
||||||
|
|
@ -14,9 +14,24 @@ import {
|
||||||
} from "../../components/ui/dialog";
|
} from "../../components/ui/dialog";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
import { Textarea } from "../../components/ui/textarea";
|
||||||
import { PROMPT_DIALOG_SUBTITLE, TEXT_DIALOG_SUBTITLE } from "../../constants";
|
import {
|
||||||
|
HIGHLIGH_CSS,
|
||||||
|
PROMPT_DIALOG_SUBTITLE,
|
||||||
|
TEXT_DIALOG_SUBTITLE,
|
||||||
|
} from "../../constants";
|
||||||
import { FileText } from "lucide-react";
|
import { FileText } from "lucide-react";
|
||||||
import { APIClassType } from "../../types/api";
|
import { APIClassType } from "../../types/api";
|
||||||
|
import {
|
||||||
|
INVALID_CHARACTERS,
|
||||||
|
TypeModal,
|
||||||
|
classNames,
|
||||||
|
getRandomKeyByssmm,
|
||||||
|
regexHighlight,
|
||||||
|
varHighlightHTML,
|
||||||
|
} from "../../utils";
|
||||||
|
import { Badge } from "../../components/ui/badge";
|
||||||
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
|
import DOMPurify from "dompurify";
|
||||||
|
|
||||||
export default function GenericModal({
|
export default function GenericModal({
|
||||||
field_name = "",
|
field_name = "",
|
||||||
|
|
@ -40,24 +55,122 @@ export default function GenericModal({
|
||||||
const [myButtonText] = useState(buttonText);
|
const [myButtonText] = useState(buttonText);
|
||||||
const [myModalTitle] = useState(modalTitle);
|
const [myModalTitle] = useState(modalTitle);
|
||||||
const [myModalType] = useState(type);
|
const [myModalType] = useState(type);
|
||||||
const [open, setOpen] = useState(true);
|
const [inputValue, setInputValue] = useState(value);
|
||||||
const [myValue, setMyValue] = useState(value);
|
const [isEdit, setIsEdit] = useState(true);
|
||||||
|
const [wordsHighlightInvalid, setWordsHighlightInvalid] = useState([]);
|
||||||
|
const [wordsHighlight, setWordsHighlight] = useState([]);
|
||||||
const { dark } = useContext(darkContext);
|
const { dark } = useContext(darkContext);
|
||||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
const { setErrorData, setSuccessData, setNoticeData } =
|
||||||
|
useContext(alertContext);
|
||||||
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
function setModalOpen(x: boolean) {
|
function setModalOpen(x: boolean) {
|
||||||
setOpen(x);
|
|
||||||
if (x === false) {
|
if (x === false) {
|
||||||
setCloseEdit("generic");
|
setCloseEdit("generic");
|
||||||
closePopUp();
|
closePopUp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkVariables(valueToCheck) {
|
||||||
|
const regex = /\{([^{}]+)\}/g;
|
||||||
|
const matches = [];
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(valueToCheck))) {
|
||||||
|
matches.push(`{${match[1]}}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let invalid_chars = [];
|
||||||
|
let fixed_variables = [];
|
||||||
|
let input_variables = matches;
|
||||||
|
for (let variable of input_variables) {
|
||||||
|
let new_var = variable;
|
||||||
|
for (let char of INVALID_CHARACTERS) {
|
||||||
|
if (variable.includes(char)) {
|
||||||
|
invalid_chars.push(new_var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixed_variables.push(new_var);
|
||||||
|
if (new_var !== variable) {
|
||||||
|
const index = input_variables.indexOf(variable);
|
||||||
|
if (index !== -1) {
|
||||||
|
input_variables.splice(index, 1, new_var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredWordsHighlight = matches.filter(
|
||||||
|
(word) => !invalid_chars.includes(word)
|
||||||
|
);
|
||||||
|
|
||||||
|
setWordsHighlightInvalid(invalid_chars);
|
||||||
|
setWordsHighlight(filteredWordsHighlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type == TypeModal.PROMPT && inputValue && inputValue != "") {
|
||||||
|
checkVariables(inputValue);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const coloredContent = (inputValue || "")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(regexHighlight, varHighlightHTML({ name: "$1" }))
|
||||||
|
.replace(/\n/g, "<br />");
|
||||||
|
|
||||||
|
const TextAreaContentView = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={HIGHLIGH_CSS}
|
||||||
|
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(coloredContent) }}
|
||||||
|
suppressContentEditableWarning={true}
|
||||||
|
onClick={() => {
|
||||||
|
setIsEdit(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function validatePrompt(closeModal: boolean) {
|
||||||
|
postValidatePrompt(field_name, inputValue, nodeClass)
|
||||||
|
.then((apiReturn) => {
|
||||||
|
if (apiReturn.data) {
|
||||||
|
setNodeClass(apiReturn.data.frontend_node);
|
||||||
|
|
||||||
|
let inputVariables = apiReturn.data.input_variables;
|
||||||
|
if (inputVariables.length === 0) {
|
||||||
|
setIsEdit(true);
|
||||||
|
setNoticeData({
|
||||||
|
title: "Your template does not have any variables.",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setIsEdit(false);
|
||||||
|
setSuccessData({
|
||||||
|
title: "Prompt is ready",
|
||||||
|
});
|
||||||
|
setModalOpen(closeModal);
|
||||||
|
setValue(inputValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsEdit(true);
|
||||||
|
setErrorData({
|
||||||
|
title: "Something went wrong, please try again",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setIsEdit(true);
|
||||||
|
return setErrorData({
|
||||||
|
title: "There is something wrong with this prompt, please review it",
|
||||||
|
list: [error.response.data.detail],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onOpenChange={setModalOpen}>
|
<Dialog open={true} onOpenChange={setModalOpen}>
|
||||||
<DialogTrigger></DialogTrigger>
|
<DialogTrigger></DialogTrigger>
|
||||||
<DialogContent className="lg:max-w-[700px]">
|
<DialogContent className="min-w-[80vw]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center">
|
<DialogTitle className="flex items-center">
|
||||||
<span className="pr-2">{myModalTitle}</span>
|
<span className="pr-2">{myModalTitle}</span>
|
||||||
|
|
@ -83,17 +196,76 @@ export default function GenericModal({
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="mt-2 flex h-full w-full">
|
{type == TypeModal.PROMPT &&
|
||||||
<Textarea
|
inputValue &&
|
||||||
ref={ref}
|
inputValue != "" &&
|
||||||
className=" form-primary h-[300px] w-full "
|
wordsHighlight.length > 0 && (
|
||||||
value={myValue}
|
<>
|
||||||
onChange={(e) => {
|
<div>
|
||||||
setMyValue(e.target.value);
|
<span className="">Variables: </span>
|
||||||
setValue(e.target.value);
|
{wordsHighlight.map((word, index) => (
|
||||||
}}
|
<ShadTooltip
|
||||||
placeholder="Type message here."
|
key={getRandomKeyByssmm() + index}
|
||||||
/>
|
content={word.replace(/[{}]/g, "")}
|
||||||
|
asChild={false}
|
||||||
|
delayDuration={1500}
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
key={index}
|
||||||
|
size="lg"
|
||||||
|
className="m-1 max-w-[40vw] cursor-default truncate p-2.5 text-sm"
|
||||||
|
>
|
||||||
|
<div className="relative bottom-[1px]">
|
||||||
|
<span>
|
||||||
|
{word.replace(/[{}]/g, "").length > 59
|
||||||
|
? word.replace(/[{}]/g, "").slice(0, 56) + "..."
|
||||||
|
: word.replace(/[{}]/g, "")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Badge>
|
||||||
|
</ShadTooltip>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
!isEdit ? "rounded-lg border" : "",
|
||||||
|
"flex h-[60vh] w-full"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{type == TypeModal.PROMPT && isEdit ? (
|
||||||
|
<Textarea
|
||||||
|
ref={ref}
|
||||||
|
className="form-input h-full w-full rounded-lg border-gray-300 focus-visible:ring-1 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
|
||||||
|
value={inputValue}
|
||||||
|
onBlur={() => {
|
||||||
|
blur();
|
||||||
|
setIsEdit(false);
|
||||||
|
}}
|
||||||
|
autoFocus
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
checkVariables(e.target.value);
|
||||||
|
}}
|
||||||
|
placeholder="Type message here."
|
||||||
|
/>
|
||||||
|
) : type == TypeModal.PROMPT && !isEdit ? (
|
||||||
|
<TextAreaContentView />
|
||||||
|
) : type != TypeModal.PROMPT ? (
|
||||||
|
<Textarea
|
||||||
|
ref={ref}
|
||||||
|
className="form-input h-full w-full rounded-lg border-gray-300 focus-visible:ring-1 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
}}
|
||||||
|
placeholder="Type message here."
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
|
|
@ -105,39 +277,7 @@ export default function GenericModal({
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
console.log("postValidatePrompt");
|
validatePrompt(false);
|
||||||
postValidatePrompt(field_name, myValue, nodeClass)
|
|
||||||
.then((apiReturn) => {
|
|
||||||
if (apiReturn.data) {
|
|
||||||
setNodeClass(apiReturn.data.frontend_node);
|
|
||||||
setModalOpen(false);
|
|
||||||
|
|
||||||
let inputVariables = apiReturn.data.input_variables;
|
|
||||||
if (inputVariables.length === 0) {
|
|
||||||
setErrorData({
|
|
||||||
title:
|
|
||||||
"The template you are attempting to use does not contain any variables for data entry.",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setSuccessData({
|
|
||||||
title: "Prompt is ready",
|
|
||||||
});
|
|
||||||
setModalOpen(false);
|
|
||||||
setValue(myValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: "Something went wrong, please try again",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return setErrorData({
|
|
||||||
title:
|
|
||||||
"There is something wrong with this prompt, please review it",
|
|
||||||
list: [error.response.data.detail],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,10 @@ export default function ImportModal() {
|
||||||
bgColor="bg-medium-emerald "
|
bgColor="bg-medium-emerald "
|
||||||
description={example.description ?? "Prebuilt Examples"}
|
description={example.description ?? "Prebuilt Examples"}
|
||||||
icon={
|
icon={
|
||||||
<DocumentDuplicateIcon strokeWidth={1.5} className="h-6 w-6 flex-shrink-0" />
|
<DocumentDuplicateIcon
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="h-6 w-6 flex-shrink-0"
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addFlow(example, false);
|
addFlow(example, false);
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
import {
|
|
||||||
XMarkIcon,
|
|
||||||
ClipboardDocumentListIcon,
|
|
||||||
} from "@heroicons/react/24/outline";
|
|
||||||
import { Fragment, useContext, useRef, useState } from "react";
|
|
||||||
import { PopUpContext } from "../../contexts/popUpContext";
|
|
||||||
|
|
||||||
export default function TextAreaModal({
|
|
||||||
value,
|
|
||||||
setValue,
|
|
||||||
}: {
|
|
||||||
setValue: (value: string) => void;
|
|
||||||
value: string | string[];
|
|
||||||
}) {
|
|
||||||
const [open, setOpen] = useState(true);
|
|
||||||
const [myValue, setMyValue] = useState(value);
|
|
||||||
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
|
||||||
const ref = useRef();
|
|
||||||
function setModalOpen(x: boolean) {
|
|
||||||
setOpen(x);
|
|
||||||
if (x === false) {
|
|
||||||
setTimeout(() => {
|
|
||||||
setCloseEdit("textarea");
|
|
||||||
closePopUp();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
|
||||||
<Dialog
|
|
||||||
as="div"
|
|
||||||
className="relative z-10"
|
|
||||||
onClose={setModalOpen}
|
|
||||||
initialFocus={ref}
|
|
||||||
>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="fixed inset-0 bg-ring bg-opacity-75 transition-opacity" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
|
||||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
>
|
|
||||||
<Dialog.Panel className="relative flex h-[600px] w-[700px] transform flex-col justify-between overflow-hidden rounded-lg bg-background text-left shadow-xl transition-all sm:my-8">
|
|
||||||
<div className=" absolute right-0 top-0 z-50 hidden pr-4 pt-4 sm:block">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="rounded-md text-ring hover:text-accent-foreground"
|
|
||||||
onClick={() => {
|
|
||||||
setModalOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Close</span>
|
|
||||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
|
||||||
<div className="z-10 flex w-full justify-center pb-4 shadow-sm">
|
|
||||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-almost-light-blue sm:mx-0 sm:h-10 sm:w-10">
|
|
||||||
<ClipboardDocumentListIcon
|
|
||||||
className="h-6 w-6 text-almost-medium-blue"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
|
||||||
<Dialog.Title
|
|
||||||
as="h3"
|
|
||||||
className="text-lg font-medium leading-10 text-foreground"
|
|
||||||
>
|
|
||||||
Edit text
|
|
||||||
</Dialog.Title>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex h-full w-full flex-row items-center justify-center gap-4 bg-input p-4">
|
|
||||||
<div className="flex h-full w-full">
|
|
||||||
<div className="w-full overflow-hidden rounded-lg bg-background px-4 py-5 shadow sm:p-6">
|
|
||||||
<textarea
|
|
||||||
ref={ref}
|
|
||||||
className="form-input h-full w-full form-primary"
|
|
||||||
value={myValue}
|
|
||||||
onChange={(e) => {
|
|
||||||
setMyValue(e.target.value);
|
|
||||||
setValue(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full flex-row-reverse bg-input px-4 pb-3">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="inline-flex w-full justify-center rounded-md border border-transparent px-4 py-2 text-base font-medium text-background shadow-sm hover:bg-ring focus:outline-none focus:ring-1 focus:ring-ring focus:ring-offset-1 sm:ml-3 sm:w-auto sm:text-sm"
|
|
||||||
onClick={() => {
|
|
||||||
setModalOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish editing
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -135,7 +135,7 @@ export default function ExtraSidebar() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full overflow-auto scrollbar-hide pb-10">
|
<div className="w-full overflow-auto pb-10 scrollbar-hide">
|
||||||
{Object.keys(dataFilter)
|
{Object.keys(dataFilter)
|
||||||
.sort()
|
.sort()
|
||||||
.map((d: keyof APIObjectType, i) =>
|
.map((d: keyof APIObjectType, i) =>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export type sendAllProps = {
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
inputs: any;
|
inputs: any;
|
||||||
|
|
||||||
chatHistory: { message: string; isSend: boolean }[];
|
chatHistory: { message: string | object; isSend: boolean }[];
|
||||||
};
|
};
|
||||||
export type errorsTypeAPI = {
|
export type errorsTypeAPI = {
|
||||||
function: { errors: Array<string> };
|
function: { errors: Array<string> };
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ import { FlowType } from "../flow";
|
||||||
|
|
||||||
export type ChatType = { flow: FlowType; reactFlowInstance: ReactFlowInstance };
|
export type ChatType = { flow: FlowType; reactFlowInstance: ReactFlowInstance };
|
||||||
export type ChatMessageType = {
|
export type ChatMessageType = {
|
||||||
message: string;
|
message: string | Object;
|
||||||
|
template?: string;
|
||||||
isSend: boolean;
|
isSend: boolean;
|
||||||
thought?: string;
|
thought?: string;
|
||||||
files?: Array<{ data: string; type: string; data_type: string }>;
|
files?: Array<{ data: string; type: string; data_type: string }>;
|
||||||
|
chatKey: string;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -142,3 +142,22 @@ export type ShadTooltipProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
style?: string;
|
style?: string;
|
||||||
};
|
};
|
||||||
|
export type ShadToolTipType = {
|
||||||
|
content?: string;
|
||||||
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
|
asChild?: boolean;
|
||||||
|
children?: ReactElement;
|
||||||
|
delayDuration?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TextHighlightType = {
|
||||||
|
value?: string;
|
||||||
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
|
asChild?: boolean;
|
||||||
|
children?: ReactElement;
|
||||||
|
delayDuration?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IVarHighlightType {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ export type TabsContextType = {
|
||||||
export type TabsState = {
|
export type TabsState = {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
isPending: boolean;
|
isPending: boolean;
|
||||||
formKeysData: {input_keys?: Object, memory_keys?: Array<string>, handle_keys?: Array<string>};
|
formKeysData: {
|
||||||
|
template?: string;
|
||||||
|
input_keys?: Object;
|
||||||
|
memory_keys?: Array<string>;
|
||||||
|
handle_keys?: Array<string>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ import {
|
||||||
import { SupabaseIcon } from "./icons/supabase";
|
import { SupabaseIcon } from "./icons/supabase";
|
||||||
import { MongoDBIcon } from "./icons/MongoDB";
|
import { MongoDBIcon } from "./icons/MongoDB";
|
||||||
import { VertexAIIcon } from "./icons/VertexAI";
|
import { VertexAIIcon } from "./icons/VertexAI";
|
||||||
|
import { IVarHighlightType } from "./types/components";
|
||||||
|
|
||||||
export function classNames(...classes: Array<string>) {
|
export function classNames(...classes: Array<string>) {
|
||||||
return classes.filter(Boolean).join(" ");
|
return classes.filter(Boolean).join(" ");
|
||||||
|
|
@ -1016,3 +1017,30 @@ export function getRandomKeyByssmm(): string {
|
||||||
const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
|
const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
|
||||||
return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001));
|
return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const INVALID_CHARACTERS = [
|
||||||
|
" ",
|
||||||
|
",",
|
||||||
|
".",
|
||||||
|
":",
|
||||||
|
";",
|
||||||
|
"!",
|
||||||
|
"?",
|
||||||
|
"/",
|
||||||
|
"\\",
|
||||||
|
"(",
|
||||||
|
")",
|
||||||
|
"[",
|
||||||
|
"]",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const regexHighlight = /\{([^}]+)\}/g;
|
||||||
|
|
||||||
|
export const varHighlightHTML = ({ name }: IVarHighlightType) => {
|
||||||
|
const html = `<div class="inline-flex items-center justify-center rounded-md font-medium text-primary bg-muted pb-1">
|
||||||
|
<span class='opacity-60 pl-1'>{</span>
|
||||||
|
<span>${name}</span>
|
||||||
|
<span class='opacity-60 pr-1'>}</span>
|
||||||
|
</div>`;
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue