Merge remote-tracking branch 'origin/release' into celery
This commit is contained in:
commit
d4beb3133c
37 changed files with 525 additions and 289 deletions
|
|
@ -7,3 +7,57 @@ import Admonition from '@theme/Admonition';
|
||||||
We appreciate your understanding as we polish our documentation – it may contain some rough edges. Share your feedback or report issues to help us improve! 🛠️📝
|
We appreciate your understanding as we polish our documentation – it may contain some rough edges. Share your feedback or report issues to help us improve! 🛠️📝
|
||||||
</p>
|
</p>
|
||||||
</Admonition>
|
</Admonition>
|
||||||
|
|
||||||
|
|
||||||
|
### BingSearchRun
|
||||||
|
|
||||||
|
Bing Search is a web search engine owned and operated by Microsoft. It provides search results for various types of content, including web pages, images, videos, and news articles. It uses a combination of algorithms and human editors to deliver search results to users.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
- **Api Wrapper:** A BingSearchAPIWrapper component that takes the search URL and a subscription key.
|
||||||
|
|
||||||
|
|
||||||
|
### Calculator
|
||||||
|
|
||||||
|
The calculator tool provides mathematical calculation capabilities to an agent by leveraging an LLMMathChain. It allows the agent to perform math when needed to answer questions.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
- **LLM:** Language Model to use in the calculation.
|
||||||
|
|
||||||
|
|
||||||
|
### GoogleSearchResults
|
||||||
|
|
||||||
|
A wrapper around Google Search. Useful for when the user needs to answer questions about with more control over the JSON data returned from the API. It returns the full JSON response configured based on the parameters passed to the API wrapper.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
- **Api Wrapper:** A GoogleSearchAPIWrapper with Google API key and CSE ID
|
||||||
|
|
||||||
|
|
||||||
|
### GoogleSearchRun
|
||||||
|
|
||||||
|
A quick wrapper around Google Search. It executes the search query and returns just the first result snippet from the highest-priority result type.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
- **Api Wrapper:** A GoogleSearchAPIWrapper with Google API key and CSE ID
|
||||||
|
|
||||||
|
|
||||||
|
### GoogleSerperRun
|
||||||
|
|
||||||
|
A low-cost Google Search API.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
- **Api Wrapper:** A GoogleSerperAPIWrapper component with API key and result keys
|
||||||
|
|
||||||
|
|
||||||
|
### InfoSQLDatabaseTool
|
||||||
|
|
||||||
|
Tool for getting metadata about a SQL database. The input to this tool is a comma-separated list of tables, and the output is the schema and sample rows for those tables. Example Input: `“table1`, `table2`, `table3”`.
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
- **Db:** SQLDatabase to query.
|
||||||
11
poetry.lock
generated
11
poetry.lock
generated
|
|
@ -7351,17 +7351,6 @@ files = [
|
||||||
{file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"},
|
{file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vine"
|
|
||||||
version = "5.0.0"
|
|
||||||
description = "Promises, promises, promises."
|
|
||||||
optional = true
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
files = [
|
|
||||||
{file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"},
|
|
||||||
{file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "watchfiles"
|
name = "watchfiles"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "langflow"
|
name = "langflow"
|
||||||
version = "0.4.7"
|
version = "0.4.10"
|
||||||
description = "A Python package with a built-in web application"
|
description = "A Python package with a built-in web application"
|
||||||
authors = ["Logspace <contact@logspace.ai>"]
|
authors = ["Logspace <contact@logspace.ai>"]
|
||||||
maintainers = [
|
maintainers = [
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from typing import Optional
|
||||||
|
from langchain.prompts import SystemMessagePromptTemplate
|
||||||
|
from langchain.tools import Tool
|
||||||
|
from langchain.schema.memory import BaseMemory
|
||||||
|
from langchain.chat_models import ChatOpenAI
|
||||||
|
|
||||||
|
from langchain.agents.agent import AgentExecutor
|
||||||
|
from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent
|
||||||
|
from langchain.memory.token_buffer import ConversationTokenBufferMemory
|
||||||
|
from langchain.prompts.chat import MessagesPlaceholder
|
||||||
|
from langchain.agents.agent_toolkits.conversational_retrieval.openai_functions import (
|
||||||
|
_get_default_system_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConversationalAgent(CustomComponent):
|
||||||
|
display_name: str = "OpenAI Conversational Agent"
|
||||||
|
description: str = "Conversational Agent that can use OpenAI's function calling API"
|
||||||
|
|
||||||
|
def build_config(self):
|
||||||
|
openai_function_models = [
|
||||||
|
"gpt-3.5-turbo-0613",
|
||||||
|
"gpt-3.5-turbo-16k-0613",
|
||||||
|
"gpt-4-0613",
|
||||||
|
"gpt-4-32k-0613",
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
"tools": {"is_list": True, "display_name": "Tools"},
|
||||||
|
"memory": {"display_name": "Memory"},
|
||||||
|
"system_message": {"display_name": "System Message"},
|
||||||
|
"max_token_limit": {"display_name": "Max Token Limit"},
|
||||||
|
"model_name": {
|
||||||
|
"display_name": "Model Name",
|
||||||
|
"options": openai_function_models,
|
||||||
|
"value": openai_function_models[0],
|
||||||
|
},
|
||||||
|
"code": {"show": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(
|
||||||
|
self,
|
||||||
|
model_name: str,
|
||||||
|
openai_api_key: str,
|
||||||
|
openai_api_base: str,
|
||||||
|
tools: Tool,
|
||||||
|
memory: Optional[BaseMemory] = None,
|
||||||
|
system_message: Optional[SystemMessagePromptTemplate] = None,
|
||||||
|
max_token_limit: int = 2000,
|
||||||
|
) -> AgentExecutor:
|
||||||
|
llm = ChatOpenAI(
|
||||||
|
model=model_name,
|
||||||
|
openai_api_key=openai_api_key,
|
||||||
|
openai_api_base=openai_api_base,
|
||||||
|
)
|
||||||
|
if not memory:
|
||||||
|
memory_key = "chat_history"
|
||||||
|
memory = ConversationTokenBufferMemory(
|
||||||
|
memory_key=memory_key,
|
||||||
|
return_messages=True,
|
||||||
|
output_key="output",
|
||||||
|
llm=llm,
|
||||||
|
max_token_limit=max_token_limit,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
memory_key = memory.memory_key # type: ignore
|
||||||
|
|
||||||
|
_system_message = system_message or _get_default_system_message()
|
||||||
|
prompt = OpenAIFunctionsAgent.create_prompt(
|
||||||
|
system_message=_system_message, # type: ignore
|
||||||
|
extra_prompt_messages=[MessagesPlaceholder(variable_name=memory_key)],
|
||||||
|
)
|
||||||
|
agent = OpenAIFunctionsAgent(
|
||||||
|
llm=llm, tools=tools, prompt=prompt # type: ignore
|
||||||
|
)
|
||||||
|
return AgentExecutor(
|
||||||
|
agent=agent,
|
||||||
|
tools=tools, # type: ignore
|
||||||
|
memory=memory,
|
||||||
|
verbose=True,
|
||||||
|
return_intermediate_steps=True,
|
||||||
|
)
|
||||||
0
src/backend/langflow/components/agents/__init__.py
Normal file
0
src/backend/langflow/components/agents/__init__.py
Normal file
|
|
@ -3,6 +3,10 @@ from typing import Any, Union
|
||||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||||
|
|
||||||
|
|
||||||
|
class UnbuiltObject:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def validate_prompt(prompt: str):
|
def validate_prompt(prompt: str):
|
||||||
"""Validate prompt."""
|
"""Validate prompt."""
|
||||||
if extract_input_variables_from_prompt(prompt):
|
if extract_input_variables_from_prompt(prompt):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import ast
|
import ast
|
||||||
|
from langflow.graph.utils import UnbuiltObject
|
||||||
from langflow.interface.initialize import loading
|
from langflow.interface.initialize import loading
|
||||||
from langflow.interface.listing import lazy_load_dict
|
from langflow.interface.listing import lazy_load_dict
|
||||||
from langflow.utils.constants import DIRECT_TYPES
|
from langflow.utils.constants import DIRECT_TYPES
|
||||||
|
|
@ -25,7 +26,7 @@ class Vertex:
|
||||||
self.edges: List["Edge"] = []
|
self.edges: List["Edge"] = []
|
||||||
self.base_type: Optional[str] = base_type
|
self.base_type: Optional[str] = base_type
|
||||||
self._parse_data()
|
self._parse_data()
|
||||||
self._built_object = None
|
self._built_object = UnbuiltObject()
|
||||||
self._built = False
|
self._built = False
|
||||||
self.artifacts: Dict[str, Any] = {}
|
self.artifacts: Dict[str, Any] = {}
|
||||||
self.task_id: Optional[str] = None
|
self.task_id: Optional[str] = None
|
||||||
|
|
@ -271,8 +272,14 @@ class Vertex:
|
||||||
"""
|
"""
|
||||||
Checks if the built object is None and raises a ValueError if so.
|
Checks if the built object is None and raises a ValueError if so.
|
||||||
"""
|
"""
|
||||||
if self._built_object is None:
|
if isinstance(self._built_object, UnbuiltObject):
|
||||||
raise ValueError(f"Node type {self.vertex_type} not found")
|
raise ValueError(f"{self.vertex_type}: {self._built_object_repr()}")
|
||||||
|
elif self._built_object is None:
|
||||||
|
message = f"{self.vertex_type} returned None."
|
||||||
|
if self.base_type == "custom_components":
|
||||||
|
message += " Make sure your build method returns a component."
|
||||||
|
|
||||||
|
raise ValueError(message)
|
||||||
|
|
||||||
def build(self, force: bool = False) -> Any:
|
def build(self, force: bool = False) -> Any:
|
||||||
if not self._built or force:
|
if not self._built or force:
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,11 @@ class PromptVertex(Vertex):
|
||||||
# so the prompt format doesn't break
|
# so the prompt format doesn't break
|
||||||
artifacts.pop("handle_keys", None)
|
artifacts.pop("handle_keys", None)
|
||||||
try:
|
try:
|
||||||
|
if not hasattr(self._built_object, "template") and hasattr(
|
||||||
|
self._built_object, "prompt"
|
||||||
|
):
|
||||||
|
template = self._built_object.prompt.template
|
||||||
|
else:
|
||||||
template = self._built_object.template
|
template = self._built_object.template
|
||||||
for key, value in artifacts.items():
|
for key, value in artifacts.items():
|
||||||
if value:
|
if value:
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@ from langchain.text_splitter import TextSplitter
|
||||||
from langchain.tools import Tool
|
from langchain.tools import Tool
|
||||||
from langchain.vectorstores.base import VectorStore
|
from langchain.vectorstores.base import VectorStore
|
||||||
from langchain.schema import BaseOutputParser
|
from langchain.schema import BaseOutputParser
|
||||||
|
from langchain.schema.memory import BaseMemory
|
||||||
|
from langchain.memory.chat_memory import BaseChatMemory
|
||||||
|
from langchain.agents.agent import AgentExecutor
|
||||||
|
|
||||||
LANGCHAIN_BASE_TYPES = {
|
LANGCHAIN_BASE_TYPES = {
|
||||||
"Chain": Chain,
|
"Chain": Chain,
|
||||||
|
"AgentExecutor": AgentExecutor,
|
||||||
"Tool": Tool,
|
"Tool": Tool,
|
||||||
"BaseLLM": BaseLLM,
|
"BaseLLM": BaseLLM,
|
||||||
"PromptTemplate": PromptTemplate,
|
"PromptTemplate": PromptTemplate,
|
||||||
|
|
@ -22,6 +25,8 @@ LANGCHAIN_BASE_TYPES = {
|
||||||
"Embeddings": Embeddings,
|
"Embeddings": Embeddings,
|
||||||
"BaseRetriever": BaseRetriever,
|
"BaseRetriever": BaseRetriever,
|
||||||
"BaseOutputParser": BaseOutputParser,
|
"BaseOutputParser": BaseOutputParser,
|
||||||
|
"BaseMemory": BaseMemory,
|
||||||
|
"BaseChatMemory": BaseChatMemory,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Langchain base types plus Python base types
|
# Langchain base types plus Python base types
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ class CustomComponent(Component, extra=Extra.allow):
|
||||||
|
|
||||||
for type_hint in TYPE_HINT_LIST:
|
for type_hint in TYPE_HINT_LIST:
|
||||||
if reader._is_type_hint_used_in_args(
|
if reader._is_type_hint_used_in_args(
|
||||||
"Optional", code
|
type_hint, code
|
||||||
) and not reader._is_type_hint_imported("Optional", code):
|
) and not reader._is_type_hint_imported(type_hint, code):
|
||||||
error_detail = {
|
error_detail = {
|
||||||
"error": "Type hint Error",
|
"error": "Type hint Error",
|
||||||
"traceback": f"Type hint '{type_hint}' is used but not imported in the code.",
|
"traceback": f"Type hint '{type_hint}' is used but not imported in the code.",
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,9 @@ def handle_partial_variables(prompt, format_kwargs: Dict):
|
||||||
}
|
}
|
||||||
# Remove handle_keys otherwise LangChain raises an error
|
# Remove handle_keys otherwise LangChain raises an error
|
||||||
partial_variables.pop("handle_keys", None)
|
partial_variables.pop("handle_keys", None)
|
||||||
|
if partial_variables and hasattr(prompt, "partial"):
|
||||||
return prompt.partial(**partial_variables)
|
return prompt.partial(**partial_variables)
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
|
||||||
def handle_variable(params: Dict, input_variable: str, format_kwargs: Dict):
|
def handle_variable(params: Dict, input_variable: str, format_kwargs: Dict):
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from langflow.api.v1.callback import (
|
||||||
)
|
)
|
||||||
from langflow.processing.process import fix_memory_inputs, format_actions
|
from langflow.processing.process import fix_memory_inputs, format_actions
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
|
from langchain.agents.agent import AgentExecutor
|
||||||
|
|
||||||
|
|
||||||
async def get_result_and_steps(langchain_object, inputs: Union[dict, str], **kwargs):
|
async def get_result_and_steps(langchain_object, inputs: Union[dict, str], **kwargs):
|
||||||
|
|
@ -20,6 +21,7 @@ async def get_result_and_steps(langchain_object, inputs: Union[dict, str], **kwa
|
||||||
# to display intermediate steps
|
# to display intermediate steps
|
||||||
langchain_object.return_intermediate_steps = True
|
langchain_object.return_intermediate_steps = True
|
||||||
try:
|
try:
|
||||||
|
if not isinstance(langchain_object, AgentExecutor):
|
||||||
fix_memory_inputs(langchain_object)
|
fix_memory_inputs(langchain_object)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"Error fixing memory inputs: {exc}")
|
logger.error(f"Error fixing memory inputs: {exc}")
|
||||||
|
|
|
||||||
|
|
@ -138,10 +138,15 @@ class Settings(BaseSettings):
|
||||||
value = json.loads(str(value))
|
value = json.loads(str(value))
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
for item in value:
|
for item in value:
|
||||||
|
if isinstance(item, Path):
|
||||||
|
item = str(item)
|
||||||
if item not in getattr(self, key):
|
if item not in getattr(self, key):
|
||||||
getattr(self, key).append(item)
|
getattr(self, key).append(item)
|
||||||
logger.debug(f"Extended {key}")
|
logger.debug(f"Extended {key}")
|
||||||
else:
|
else:
|
||||||
|
if isinstance(value, Path):
|
||||||
|
value = str(value)
|
||||||
|
if value not in getattr(self, key):
|
||||||
getattr(self, key).append(value)
|
getattr(self, key).append(value)
|
||||||
logger.debug(f"Appended {key}")
|
logger.debug(f"Appended {key}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import ErrorAlert from "./alerts/error";
|
||||||
import NoticeAlert from "./alerts/notice";
|
import NoticeAlert from "./alerts/notice";
|
||||||
import SuccessAlert from "./alerts/success";
|
import SuccessAlert from "./alerts/success";
|
||||||
import CrashErrorComponent from "./components/CrashErrorComponent";
|
import CrashErrorComponent from "./components/CrashErrorComponent";
|
||||||
import Header from "./components/headerComponent";
|
import LoadingComponent from "./components/loadingComponent";
|
||||||
import { alertContext } from "./contexts/alertContext";
|
import { alertContext } from "./contexts/alertContext";
|
||||||
import { locationContext } from "./contexts/locationContext";
|
import { locationContext } from "./contexts/locationContext";
|
||||||
import { TabsContext } from "./contexts/tabsContext";
|
import { TabsContext } from "./contexts/tabsContext";
|
||||||
|
|
@ -25,6 +25,7 @@ export default function App() {
|
||||||
setIsStackedOpen(true);
|
setIsStackedOpen(true);
|
||||||
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
|
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
|
||||||
const { hardReset } = useContext(TabsContext);
|
const { hardReset } = useContext(TabsContext);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
errorData,
|
errorData,
|
||||||
errorOpen,
|
errorOpen,
|
||||||
|
|
@ -35,6 +36,7 @@ export default function App() {
|
||||||
successData,
|
successData,
|
||||||
successOpen,
|
successOpen,
|
||||||
setSuccessOpen,
|
setSuccessOpen,
|
||||||
|
loading,
|
||||||
} = useContext(alertContext);
|
} = useContext(alertContext);
|
||||||
|
|
||||||
// Initialize state variable for the list of alerts
|
// Initialize state variable for the list of alerts
|
||||||
|
|
@ -133,8 +135,15 @@ export default function App() {
|
||||||
}}
|
}}
|
||||||
FallbackComponent={CrashErrorComponent}
|
FallbackComponent={CrashErrorComponent}
|
||||||
>
|
>
|
||||||
<Header />
|
{loading ? (
|
||||||
|
<div className="loading-page-panel">
|
||||||
|
<LoadingComponent remSize={50} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<Router />
|
<Router />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<div></div>
|
<div></div>
|
||||||
<div className="app-div" style={{ zIndex: 999 }}>
|
<div className="app-div" style={{ zIndex: 999 }}>
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,13 @@ export default function AccordionComponent({
|
||||||
value === "" ? setValue(keyValue) : setValue("");
|
value === "" ? setValue(keyValue) : setValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleKeyDown = (event) => {
|
||||||
|
if (event.key === "Backspace") {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Accordion
|
<Accordion
|
||||||
|
|
@ -39,6 +46,7 @@ export default function AccordionComponent({
|
||||||
className="w-full"
|
className="w-full"
|
||||||
value={value}
|
value={value}
|
||||||
onValueChange={setValue}
|
onValueChange={setValue}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
>
|
>
|
||||||
<AccordionItem value={keyValue} className="border-b">
|
<AccordionItem value={keyValue} className="border-b">
|
||||||
<AccordionTrigger
|
<AccordionTrigger
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ type InputProps = {
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
flows: Array<{ id: string; name: string; description: string }>;
|
flows: Array<{ id: string; name: string; description: string }>;
|
||||||
tabId: string;
|
tabId: string;
|
||||||
invalidName: boolean;
|
invalidName?: boolean;
|
||||||
setInvalidName: (invalidName: boolean) => void;
|
setInvalidName?: (invalidName: boolean) => void;
|
||||||
setName: (name: string) => void;
|
setName: (name: string) => void;
|
||||||
setDescription: (description: string) => void;
|
setDescription: (description: string) => void;
|
||||||
updateFlow: (flow: { id: string; name: string }) => void;
|
updateFlow: (flow: { id: string; name: string }) => void;
|
||||||
|
|
@ -46,21 +46,29 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
||||||
} else {
|
} else {
|
||||||
setIsMaxLength(false);
|
setIsMaxLength(false);
|
||||||
}
|
}
|
||||||
|
if (invalidName !== undefined) {
|
||||||
if (!nameLists.current.includes(value)) {
|
if (!nameLists.current.includes(value)) {
|
||||||
setInvalidName(false);
|
setInvalidName(false);
|
||||||
} else {
|
} else {
|
||||||
setInvalidName(true);
|
setInvalidName(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setName(value);
|
setName(value);
|
||||||
|
setCurrentName(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [desc, setDesc] = useState(
|
const [currentName, setCurrentName] = useState(name);
|
||||||
flows.find((flow) => flow.id === tabId).description
|
|
||||||
);
|
const [currentDescription, setCurrentDescription] = useState(description);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentName(name);
|
||||||
|
setCurrentDescription(description);
|
||||||
|
}, [name, description]);
|
||||||
|
|
||||||
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
flows.find((flow) => flow.id === tabId).description = event.target.value;
|
flows.find((f) => f.id === tabId).description = event.target.value;
|
||||||
setDesc(flows.find((flow) => flow.id === tabId).description);
|
setCurrentDescription(flows.find((f) => f.id === tabId).description);
|
||||||
setDescription(event.target.value);
|
setDescription(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -81,7 +89,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
value={name ?? ""}
|
value={currentName ?? ""}
|
||||||
placeholder="File name"
|
placeholder="File name"
|
||||||
id="name"
|
id="name"
|
||||||
maxLength={maxLength}
|
maxLength={maxLength}
|
||||||
|
|
@ -96,7 +104,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
||||||
name="description"
|
name="description"
|
||||||
id="description"
|
id="description"
|
||||||
onChange={handleDescriptionChange}
|
onChange={handleDescriptionChange}
|
||||||
value={desc}
|
value={currentDescription}
|
||||||
placeholder="Flow description"
|
placeholder="Flow description"
|
||||||
className="mt-2 max-h-[100px] font-normal"
|
className="mt-2 max-h-[100px] font-normal"
|
||||||
rows={3}
|
rows={3}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { FloatComponentType } from "../../types/components";
|
import { FloatComponentType } from "../../types/components";
|
||||||
|
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
|
|
||||||
export default function FloatComponent({
|
export default function FloatComponent({
|
||||||
|
|
@ -43,6 +44,9 @@ export default function FloatComponent({
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
onChange(event.target.value);
|
onChange(event.target.value);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
handleKeyDown(e, value, "0");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { InputComponentType } from "../../types/components";
|
import { InputComponentType } from "../../types/components";
|
||||||
|
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||||
import { classNames } from "../../utils/utils";
|
import { classNames } from "../../utils/utils";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
|
|
||||||
|
|
@ -34,6 +35,9 @@ export default function InputComponent({
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
onChange(event.target.value);
|
onChange(event.target.value);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
handleKeyDown(e, value, "");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{password && (
|
{password && (
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,12 @@ export default function InputListComponent({
|
||||||
newInputList[idx] = event.target.value;
|
newInputList[idx] = event.target.value;
|
||||||
onChange(newInputList);
|
onChange(newInputList);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.ctrlKey && e.key === "Backspace") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{idx === value.length - 1 ? (
|
{idx === value.length - 1 ? (
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { FloatComponentType } from "../../types/components";
|
import { FloatComponentType } from "../../types/components";
|
||||||
|
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
|
|
||||||
export default function IntComponent({
|
export default function IntComponent({
|
||||||
|
|
@ -37,6 +38,7 @@ export default function IntComponent({
|
||||||
) {
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
handleKeyDown(event, value, "0");
|
||||||
}}
|
}}
|
||||||
type="number"
|
type="number"
|
||||||
step="1"
|
step="1"
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,4 @@ const PopoverContent = React.forwardRef<
|
||||||
));
|
));
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Popover, PopoverTrigger, PopoverContent };
|
export { Popover, PopoverContent, PopoverTrigger };
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export const INVALID_CHARACTERS = [
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const regexHighlight = /\{([^}]+)\}/g;
|
export const regexHighlight = /\{([^}]+)\}/g;
|
||||||
|
export const specialCharsRegex = /[!@#$%^&*()\-_=+[\]{}|;:'",.<>/?\\`´]/;
|
||||||
|
|
||||||
export const programmingLanguages: languageMap = {
|
export const programmingLanguages: languageMap = {
|
||||||
javascript: ".js",
|
javascript: ".js",
|
||||||
|
|
@ -509,83 +510,4 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
|
||||||
"/api/v1/validate/prompt",
|
"/api/v1/validate/prompt",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const tabsCode = [];
|
export const skipNodeUpdate = ["CustomComponent"];
|
||||||
|
|
||||||
export function tabsArray(codes: string[], method: number) {
|
|
||||||
if (!method) return;
|
|
||||||
if (method === 0) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: "cURL",
|
|
||||||
mode: "bash",
|
|
||||||
image: "https://curl.se/logo/curl-symbol-transparent.png",
|
|
||||||
language: "sh",
|
|
||||||
code: codes[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Python API",
|
|
||||||
mode: "python",
|
|
||||||
image:
|
|
||||||
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
|
|
||||||
language: "py",
|
|
||||||
code: codes[1],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Python Code",
|
|
||||||
mode: "python",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
language: "py",
|
|
||||||
code: codes[2],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Chat Widget HTML",
|
|
||||||
description:
|
|
||||||
"Insert this code anywhere in your <body> tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
|
|
||||||
mode: "html",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
language: "py",
|
|
||||||
code: codes[3],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: "cURL",
|
|
||||||
mode: "bash",
|
|
||||||
image: "https://curl.se/logo/curl-symbol-transparent.png",
|
|
||||||
language: "sh",
|
|
||||||
code: codes[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Python API",
|
|
||||||
mode: "python",
|
|
||||||
image:
|
|
||||||
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
|
|
||||||
language: "py",
|
|
||||||
code: codes[1],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Python Code",
|
|
||||||
mode: "python",
|
|
||||||
language: "py",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
code: codes[2],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Chat Widget HTML",
|
|
||||||
description:
|
|
||||||
"Insert this code anywhere in your <body> tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
|
|
||||||
mode: "html",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
language: "py",
|
|
||||||
code: codes[3],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Tweaks",
|
|
||||||
mode: "python",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
language: "py",
|
|
||||||
code: codes[4],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,16 @@ type alertContextType = {
|
||||||
pushNotificationList: (Object: AlertItemType) => void;
|
pushNotificationList: (Object: AlertItemType) => void;
|
||||||
clearNotificationList: () => void;
|
clearNotificationList: () => void;
|
||||||
removeFromNotificationList: (index: string) => void;
|
removeFromNotificationList: (index: string) => void;
|
||||||
|
loading: boolean;
|
||||||
|
setLoading: (newState: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
//initial values to alertContextType
|
//initial values to alertContextType
|
||||||
const initialValue: alertContextType = {
|
const initialValue: alertContextType = {
|
||||||
errorData: { title: "", list: [] },
|
errorData: { title: "", list: [] },
|
||||||
setErrorData: () => {},
|
setErrorData: () => {},
|
||||||
|
loading: true,
|
||||||
|
setLoading: () => {},
|
||||||
errorOpen: false,
|
errorOpen: false,
|
||||||
setErrorOpen: () => {},
|
setErrorOpen: () => {},
|
||||||
noticeData: { title: "", link: "" },
|
noticeData: { title: "", link: "" },
|
||||||
|
|
@ -55,6 +59,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
||||||
list?: Array<string>;
|
list?: Array<string>;
|
||||||
}>({ title: "", list: [] });
|
}>({ title: "", list: [] });
|
||||||
const [errorOpen, setErrorOpen] = useState(false);
|
const [errorOpen, setErrorOpen] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
const [noticeData, setNoticeDataState] = useState<{
|
const [noticeData, setNoticeDataState] = useState<{
|
||||||
title: string;
|
title: string;
|
||||||
link?: string;
|
link?: string;
|
||||||
|
|
@ -141,6 +146,8 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
||||||
removeFromNotificationList,
|
removeFromNotificationList,
|
||||||
clearNotificationList,
|
clearNotificationList,
|
||||||
notificationList,
|
notificationList,
|
||||||
|
loading,
|
||||||
|
setLoading,
|
||||||
pushNotificationList,
|
pushNotificationList,
|
||||||
setNotificationCenter,
|
setNotificationCenter,
|
||||||
notificationCenter,
|
notificationCenter,
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,17 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<DarkProvider>
|
<DarkProvider>
|
||||||
|
<AlertProvider>
|
||||||
<TypesProvider>
|
<TypesProvider>
|
||||||
<LocationProvider>
|
<LocationProvider>
|
||||||
<AlertProvider>
|
|
||||||
<SSEProvider>
|
<SSEProvider>
|
||||||
<TabsProvider>
|
<TabsProvider>
|
||||||
<UndoRedoProvider>{children}</UndoRedoProvider>
|
<UndoRedoProvider>{children}</UndoRedoProvider>
|
||||||
</TabsProvider>
|
</TabsProvider>
|
||||||
</SSEProvider>
|
</SSEProvider>
|
||||||
</AlertProvider>
|
|
||||||
</LocationProvider>
|
</LocationProvider>
|
||||||
</TypesProvider>
|
</TypesProvider>
|
||||||
|
</AlertProvider>
|
||||||
</DarkProvider>
|
</DarkProvider>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
} from "react";
|
} from "react";
|
||||||
import { addEdge } from "reactflow";
|
import { addEdge } from "reactflow";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
|
import { skipNodeUpdate } from "../constants/constants";
|
||||||
import {
|
import {
|
||||||
deleteFlowFromDatabase,
|
deleteFlowFromDatabase,
|
||||||
downloadFlowsFromDatabase,
|
downloadFlowsFromDatabase,
|
||||||
|
|
@ -163,6 +164,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
function processFlowNodes(flow) {
|
function processFlowNodes(flow) {
|
||||||
if (!flow.data || !flow.data.nodes) return;
|
if (!flow.data || !flow.data.nodes) return;
|
||||||
flow.data.nodes.forEach((node: NodeType) => {
|
flow.data.nodes.forEach((node: NodeType) => {
|
||||||
|
if (skipNodeUpdate.includes(node.data.type)) return;
|
||||||
const template = templates[node.data.type];
|
const template = templates[node.data.type];
|
||||||
if (!template) {
|
if (!template) {
|
||||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||||
|
|
@ -508,6 +510,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
|
|
||||||
const updateNodes = (nodes, edges) => {
|
const updateNodes = (nodes, edges) => {
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
|
if (skipNodeUpdate.includes(node.data.type)) return;
|
||||||
const template = templates[node.data.type];
|
const template = templates[node.data.type];
|
||||||
if (!template) {
|
if (!template) {
|
||||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
import { createContext, ReactNode, useEffect, useState } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { Node } from "reactflow";
|
import { Node } from "reactflow";
|
||||||
import { getAll } from "../controllers/API";
|
import { getAll } from "../controllers/API";
|
||||||
import { APIKindType } from "../types/api";
|
import { APIKindType } from "../types/api";
|
||||||
import { typesContextType } from "../types/typesContext";
|
import { typesContextType } from "../types/typesContext";
|
||||||
|
import { alertContext } from "./alertContext";
|
||||||
|
|
||||||
//context to share types adn functions from nodes to flow
|
//context to share types adn functions from nodes to flow
|
||||||
|
|
||||||
|
|
@ -25,6 +32,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
||||||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||||
const [templates, setTemplates] = useState({});
|
const [templates, setTemplates] = useState({});
|
||||||
const [data, setData] = useState({});
|
const [data, setData] = useState({});
|
||||||
|
const { setLoading } = useContext(alertContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let delay = 1000; // Start delay of 1 second
|
let delay = 1000; // Start delay of 1 second
|
||||||
|
|
@ -40,6 +48,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
||||||
const result = await getAll();
|
const result = await getAll();
|
||||||
// Make sure to only update the state if the component is still mounted.
|
// Make sure to only update the state if the component is still mounted.
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
|
setLoading(false);
|
||||||
setData(result.data);
|
setData(result.data);
|
||||||
setTemplates(
|
setTemplates(
|
||||||
Object.keys(result.data).reduce((acc, curr) => {
|
Object.keys(result.data).reduce((acc, curr) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ReactNode, forwardRef, useContext, useState } from "react";
|
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
|
||||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
|
|
@ -9,17 +9,17 @@ import { removeApiKeys } from "../../utils/reactflowUtils";
|
||||||
import BaseModal from "../baseModal";
|
import BaseModal from "../baseModal";
|
||||||
|
|
||||||
const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
|
const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
|
||||||
const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
|
const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
|
||||||
useContext(TabsContext);
|
|
||||||
const [checked, setChecked] = useState(false);
|
const [checked, setChecked] = useState(false);
|
||||||
const [name, setName] = useState(
|
const flow = flows.find((f) => f.id === tabId);
|
||||||
flows.find((flow) => flow.id === tabId).name
|
useEffect(() => {
|
||||||
);
|
setName(flow.name);
|
||||||
const [invalidName, setInvalidName] = useState(false);
|
setDescription(flow.description);
|
||||||
const [description, setDescription] = useState(
|
}, [flow.name, flow.description]);
|
||||||
flows.find((flow) => flow.id === tabId).description
|
const [name, setName] = useState(flow.name);
|
||||||
);
|
const [description, setDescription] = useState(flow.description);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseModal size="smaller" open={open} setOpen={setOpen}>
|
<BaseModal size="smaller" open={open} setOpen={setOpen}>
|
||||||
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
|
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useContext, useRef, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
|
|
@ -14,17 +14,15 @@ export default function FlowSettingsModal({
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: (open: boolean) => void;
|
setOpen: (open: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
const { setSuccessData } = useContext(alertContext);
|
||||||
const ref = useRef();
|
const { flows, tabId, updateFlow, saveFlow } = useContext(TabsContext);
|
||||||
const { flows, tabId, updateFlow, setTabsState, saveFlow } =
|
const flow = flows.find((f) => f.id === tabId);
|
||||||
useContext(TabsContext);
|
useEffect(() => {
|
||||||
const maxLength = 50;
|
setName(flow.name);
|
||||||
const [name, setName] = useState(
|
setDescription(flow.description);
|
||||||
flows.find((flow) => flow.id === tabId).name
|
}, [flow.name, flow.description]);
|
||||||
);
|
const [name, setName] = useState(flow.name);
|
||||||
const [description, setDescription] = useState(
|
const [description, setDescription] = useState(flow.description);
|
||||||
flows.find((flow) => flow.id === tabId).description
|
|
||||||
);
|
|
||||||
const [invalidName, setInvalidName] = useState(false);
|
const [invalidName, setInvalidName] = useState(false);
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import { TypeModal } from "../../constants/enums";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
import { postValidatePrompt } from "../../controllers/API";
|
import { postValidatePrompt } from "../../controllers/API";
|
||||||
import { APIClassType } from "../../types/api";
|
import { APIClassType } from "../../types/api";
|
||||||
|
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||||
import {
|
import {
|
||||||
classNames,
|
classNames,
|
||||||
getRandomKeyByssmm,
|
getRandomKeyByssmm,
|
||||||
|
|
@ -213,6 +214,9 @@ export default function GenericModal({
|
||||||
checkVariables(event.target.value);
|
checkVariables(event.target.value);
|
||||||
}}
|
}}
|
||||||
placeholder="Type message here."
|
placeholder="Type message here."
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
handleKeyDown(e, inputValue, "");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : type === TypeModal.PROMPT && !isEdit ? (
|
) : type === TypeModal.PROMPT && !isEdit ? (
|
||||||
<TextAreaContentView />
|
<TextAreaContentView />
|
||||||
|
|
@ -225,6 +229,9 @@ export default function GenericModal({
|
||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
}}
|
}}
|
||||||
placeholder="Type message here."
|
placeholder="Type message here."
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
handleKeyDown(e, value, "");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { CardComponent } from "../../components/cardComponent";
|
import { CardComponent } from "../../components/cardComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
import Header from "../../components/headerComponent";
|
||||||
import { getExamples } from "../../controllers/API";
|
import { getExamples } from "../../controllers/API";
|
||||||
import { FlowType } from "../../types/flow";
|
import { FlowType } from "../../types/flow";
|
||||||
export default function CommunityPage() {
|
export default function CommunityPage() {
|
||||||
|
|
@ -42,6 +43,9 @@ export default function CommunityPage() {
|
||||||
handleExamples();
|
handleExamples();
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
|
||||||
<div className="community-page-arrangement">
|
<div className="community-page-arrangement">
|
||||||
<div className="community-page-nav-arrangement">
|
<div className="community-page-nav-arrangement">
|
||||||
<span className="community-page-nav-title">
|
<span className="community-page-nav-title">
|
||||||
|
|
@ -98,5 +102,6 @@ export default function CommunityPage() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,13 @@ const nodeTypes = {
|
||||||
genericNode: GenericNode,
|
genericNode: GenericNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Page({ flow }: { flow: FlowType }) {
|
export default function Page({
|
||||||
|
flow,
|
||||||
|
view,
|
||||||
|
}: {
|
||||||
|
flow: FlowType;
|
||||||
|
view?: boolean;
|
||||||
|
}) {
|
||||||
let {
|
let {
|
||||||
updateFlow,
|
updateFlow,
|
||||||
uploadFlow,
|
uploadFlow,
|
||||||
|
|
@ -360,7 +366,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full overflow-hidden">
|
<div className="flex h-full overflow-hidden">
|
||||||
<ExtraSidebar />
|
{!view && <ExtraSidebar />}
|
||||||
{/* Main area */}
|
{/* Main area */}
|
||||||
<main className="flex flex-1">
|
<main className="flex flex-1">
|
||||||
{/* Primary column */}
|
{/* Primary column */}
|
||||||
|
|
@ -402,14 +408,21 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
className="theme-attribution"
|
className="theme-attribution"
|
||||||
minZoom={0.01}
|
minZoom={0.01}
|
||||||
maxZoom={8}
|
maxZoom={8}
|
||||||
|
zoomOnScroll={!view}
|
||||||
|
zoomOnPinch={!view}
|
||||||
|
panOnDrag={!view}
|
||||||
>
|
>
|
||||||
<Background className="" />
|
<Background className="" />
|
||||||
|
{!view && (
|
||||||
<Controls
|
<Controls
|
||||||
className="bg-muted fill-foreground stroke-foreground text-primary
|
className="bg-muted fill-foreground stroke-foreground text-primary
|
||||||
[&>button]:border-b-border hover:[&>button]:bg-border"
|
[&>button]:border-b-border hover:[&>button]:bg-border"
|
||||||
></Controls>
|
></Controls>
|
||||||
|
)}
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
|
{!view && (
|
||||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import Header from "../../components/headerComponent";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { getVersion } from "../../controllers/API";
|
import { getVersion } from "../../controllers/API";
|
||||||
import Page from "./components/PageComponent";
|
import Page from "./components/PageComponent";
|
||||||
|
|
@ -22,6 +23,8 @@ export default function FlowPage() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
<div className="flow-page-positioning">
|
<div className="flow-page-positioning">
|
||||||
{flows.length > 0 &&
|
{flows.length > 0 &&
|
||||||
tabId !== "" &&
|
tabId !== "" &&
|
||||||
|
|
@ -37,5 +40,6 @@ export default function FlowPage() {
|
||||||
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
|
<div className={version ? "mt-2" : "mt-1"}>Created by Logspace</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useContext, useEffect } from "react";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { CardComponent } from "../../components/cardComponent";
|
import { CardComponent } from "../../components/cardComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
import Header from "../../components/headerComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
|
@ -17,6 +18,8 @@ export default function HomePage() {
|
||||||
|
|
||||||
// Personal flows display
|
// Personal flows display
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
<div className="main-page-panel">
|
<div className="main-page-panel">
|
||||||
<div className="main-page-nav-arrangement">
|
<div className="main-page-nav-arrangement">
|
||||||
<span className="main-page-nav-title">
|
<span className="main-page-nav-title">
|
||||||
|
|
@ -86,5 +89,6 @@ export default function HomePage() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
src/frontend/src/pages/ViewPage/index.tsx
Normal file
33
src/frontend/src/pages/ViewPage/index.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
import { getVersion } from "../../controllers/API";
|
||||||
|
import Page from "../FlowPage/components/PageComponent";
|
||||||
|
|
||||||
|
export default function ViewPage() {
|
||||||
|
const { flows, tabId, setTabId } = useContext(TabsContext);
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
// Set flow tab id
|
||||||
|
useEffect(() => {
|
||||||
|
setTabId(id);
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
// Initialize state variable for the version
|
||||||
|
const [version, setVersion] = useState("");
|
||||||
|
useEffect(() => {
|
||||||
|
getVersion().then((data) => {
|
||||||
|
setVersion(data.version);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flow-page-positioning">
|
||||||
|
{flows.length > 0 &&
|
||||||
|
tabId !== "" &&
|
||||||
|
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
|
||||||
|
<Page view flow={flows.find((flow) => flow.id === tabId)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import { Route, Routes } from "react-router-dom";
|
||||||
import CommunityPage from "./pages/CommunityPage";
|
import CommunityPage from "./pages/CommunityPage";
|
||||||
import FlowPage from "./pages/FlowPage";
|
import FlowPage from "./pages/FlowPage";
|
||||||
import HomePage from "./pages/MainPage";
|
import HomePage from "./pages/MainPage";
|
||||||
|
import ViewPage from "./pages/ViewPage";
|
||||||
|
|
||||||
const Router = () => {
|
const Router = () => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -10,6 +11,7 @@ const Router = () => {
|
||||||
<Route path="/community" element={<CommunityPage />} />
|
<Route path="/community" element={<CommunityPage />} />
|
||||||
<Route path="/flow/:id/">
|
<Route path="/flow/:id/">
|
||||||
<Route path="" element={<FlowPage />} />
|
<Route path="" element={<FlowPage />} />
|
||||||
|
<Route path="view" element={<ViewPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<HomePage />} />
|
<Route path="*" element={<HomePage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,10 @@
|
||||||
@apply flex w-full;
|
@apply flex w-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-page-panel {
|
||||||
|
@apply h-full w-full flex justify-center items-center bg-muted;
|
||||||
|
}
|
||||||
|
|
||||||
.main-page-panel {
|
.main-page-panel {
|
||||||
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { Connection, Edge, ReactFlowInstance } from "reactflow";
|
import { Connection, Edge, ReactFlowInstance } from "reactflow";
|
||||||
|
import { specialCharsRegex } from "../constants/constants";
|
||||||
import { APITemplateType } from "../types/api";
|
import { APITemplateType } from "../types/api";
|
||||||
import { FlowType, NodeType } from "../types/flow";
|
import { FlowType, NodeType } from "../types/flow";
|
||||||
import { cleanEdgesType } from "../types/utils/reactflowUtils";
|
import { cleanEdgesType } from "../types/utils/reactflowUtils";
|
||||||
|
|
@ -233,6 +234,34 @@ export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
|
||||||
return newName;
|
return newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function handleKeyDown(
|
||||||
|
e: React.KeyboardEvent<HTMLInputElement>,
|
||||||
|
inputValue: string | string[] | null,
|
||||||
|
block: string
|
||||||
|
) {
|
||||||
|
console.log(e, inputValue, block);
|
||||||
|
//condition to fix bug control+backspace on Windows/Linux
|
||||||
|
if (
|
||||||
|
(typeof inputValue === "string" &&
|
||||||
|
(e.metaKey === true || e.ctrlKey === true) &&
|
||||||
|
e.key === "Backspace" &&
|
||||||
|
(inputValue === block ||
|
||||||
|
inputValue?.charAt(inputValue?.length - 1) === " " ||
|
||||||
|
specialCharsRegex.test(inputValue?.charAt(inputValue?.length - 1)))) ||
|
||||||
|
(navigator.userAgent.toUpperCase().includes("MAC") &&
|
||||||
|
e.ctrlKey === true &&
|
||||||
|
e.key === "Backspace")
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.ctrlKey === true && e.key === "Backspace" && inputValue === block) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getConnectedNodes(
|
export function getConnectedNodes(
|
||||||
edge: Edge,
|
edge: Edge,
|
||||||
nodes: Array<NodeType>
|
nodes: Array<NodeType>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue