Merge branch 'dev' into docs-explain-io-playground

This commit is contained in:
Mendon Kissling 2024-06-07 13:48:43 -04:00
commit 25307e7509
62 changed files with 1276 additions and 962 deletions

View file

@ -80,7 +80,10 @@ jobs:
langflowai/langflow-frontend:1.0-alpha
restart-space:
name: Restart HuggingFace Spaces
if: ${{ inputs.release_type == 'main' }}
runs-on: ubuntu-latest
needs: docker_build
strategy:
matrix:
python-version:

View file

@ -35,6 +35,10 @@ jobs:
with:
python-version: "3.10"
cache: "poetry"
- name: Set up Nodejs 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Check Version
id: check-version
run: |

View file

@ -168,6 +168,7 @@ build_and_install:
build_frontend:
cd src/frontend && CI='' npm run build
rm -rf src/backend/base/langflow/frontend
cp -r src/frontend/build src/backend/base/langflow/frontend
build:

View file

@ -1,6 +1,7 @@
# syntax=docker/dockerfile:1
# Keep this syntax directive! It's used to enable Docker BuildKit
################################
# BUILDER-BASE
# Used to build deps + create our virtual environment
@ -47,12 +48,10 @@ WORKDIR /app
COPY pyproject.toml poetry.lock README.md ./
COPY src/ ./src
COPY scripts/ ./scripts
RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py
RUN $POETRY_HOME/bin/poetry lock --no-update \
&& $POETRY_HOME/bin/poetry install --no-interaction --no-ansi -E deploy \
&& $POETRY_HOME/bin/poetry build -f wheel \
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl --force-reinstall
################################
# RUNTIME

View file

@ -10,8 +10,7 @@ Langflow provides an API key functionality that allows users to access their ind
The default user and password are set using the LANGFLOW_SUPERUSER and
LANGFLOW_SUPERUSER_PASSWORD environment variables.
The default values are
langflow and langflow, respectively.
The default values are `langflow` and `langflow`, respectively.
</Admonition>

View file

@ -1,62 +1,51 @@
# Command Line Interface (CLI)
## Overview
Langflow's Command Line Interface (CLI) is a powerful tool that allows you to interact with the Langflow server from the command line. The CLI provides a wide range of commands to help you shape Langflow to your needs.
Running the CLI without any arguments will display a list of available commands and options.
The available commands are below. Navigate to their individual sections of this page to see the parameters.
* [langflow](#overview)
* [langflow api-key](#langflow-api-key)
* [langflow copy-db](#langflow-copy-db)
* [langflow migration](#langflow-migration)
* [langflow run](#langflow-run)
* [langflow superuser](#langflow-superuser)
## Overview
Running the CLI without any arguments displays a list of available options and commands.
```bash
python -m langflow run --help
langflow
# or
python -m langflow run
langflow --help
# or
python -m langflow
```
Each option for `run` command are detailed below:
| Command | Description |
| ------- | ----------- |
| `api-key` | Creates an API key for the default superuser if AUTO_LOGIN is enabled. |
| `copy-db` | Copy the database files to the current directory (`which langflow`). |
| `migration` | Run or test migrations. |
| `run` | Run the Langflow. |
| `superuser` | Create a superuser. |
- `--help`: Displays all available options.
- `--host`: Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`.
- `--workers`: Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`.
- `--timeout`: Sets the worker timeout in seconds. The default is `60`.
- `--port`: Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`.
- `--env-file`: Specifies the path to the .env file containing environment variables. The default is `.env`.
- `--log-level`: Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`.
- `--components-path`: Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`.
- `--log-file`: Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`.
- `--cache`: Select the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`.
- `--dev/--no-dev`: Toggles the development mode. The default is `no-dev`.
- `--path`: Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable.
- `--open-browser/--no-open-browser`: Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`.
- `--remove-api-keys/--no-remove-api-keys`: Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`.
- `--install-completion [bash|zsh|fish|powershell|pwsh]`: Installs completion for the specified shell.
- `--show-completion [bash|zsh|fish|powershell|pwsh]`: Shows completion for the specified shell, allowing you to copy it or customize the installation.
- `--backend-only`: This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable.
- `--store`: This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable.
### Options
These parameters are important for users who need to customize the behavior of Langflow, especially in development or specialized deployment scenarios.
| Option | Description |
| ------ | ----------- |
| `--install-completion` | Install completion for the current shell. |
| `--show-completion` | Show completion for the current shell, to copy it or customize the installation. |
| `--help` | Show this message and exit. |
### API Key Command
## langflow api-key
The `api-key` command allows you to create an API key for accessing Langflow's API when `LANGFLOW_AUTO_LOGIN` is set to `True`.
```bash
python -m langflow api-key --help
Usage: langflow api-key [OPTIONS]
Creates an API key for the default superuser if AUTO_LOGIN is enabled.
Args: log_level (str, optional): Logging level. Defaults to "error".
Returns: None
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --log-level TEXT Logging level. [env var: LANGFLOW_LOG_LEVEL] [default: error] │
│ --help Show this message and exit. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
Once you run the `api-key` command, it will create an API key for the default superuser if `LANGFLOW_AUTO_LOGIN` is set to `True`.
Run the `api-key` command to create an API key for the default superuser if `LANGFLOW_AUTO_LOGIN` is set to `True`.
```bash
langflow api-key
# or
python -m langflow api-key
╭─────────────────────────────────────────────────────────────────────╮
│ API Key Created Successfully: │
@ -67,11 +56,99 @@ python -m langflow api-key
│ Make sure to store it in a secure location. │
│ │
│ The API key has been copied to your clipboard. Cmd + V to paste it. │
╰─────────────────────────────────────────────────────────────────────╯
╰──────────────────────────────
```
### Environment Variables
### Options
| Option | Type | Description |
|------------------|------|-------------------------------------------------------------|
| --log-level | TEXT | Logging level. [env var: LANGFLOW_LOG_LEVEL] [default: error] |
| --help | | Show this message and exit. |
## langflow copy-db
Run the `copy-db` command to copy the cached `langflow.db` and `langflow-pre.db` database files to the current directory.
If the files exist in the cache directory, they will be copied to the same directory as `__main__.py`, which can be found with `which langflow`.
### Options
None.
## langflow migration
Run or test migrations with the [Alembic](https://pypi.org/project/alembic/) database tool.
```bash
langflow migration
# or
python -m langflow migration
```
### Options
| Option | Description |
|-----------------|-------------------------------------------------------------|
| `--test, --no-test` | Run migrations in test mode. [default: test] |
| `--fix, --no-fix` | Fix migrations. This is a destructive operation, and should only be used if you know what you are doing. [default: no-fix] |
| `--help` | Show this message and exit. |
## langflow run
Run Langflow.
```bash
langflow run
# or
python -m langflow run
```
### Options
| Option | Description |
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--help` | Displays all available options. |
| `--host` | Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`. |
| `--workers` | Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`. |
| `--timeout` | Sets the worker timeout in seconds. The default is `60`. |
| `--port` | Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`. |
| `--env-file` | Specifies the path to the .env file containing environment variables. The default is `.env`. |
| `--log-level` | Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`. |
| `--components-path` | Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`. |
| `--log-file` | Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`. |
| `--cache` | Select the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`. |
| `--dev`/`--no-dev` | Toggles the development mode. The default is `no-dev`. |
| `--path` | Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable. |
| `--open-browser`/`--no-open-browser`| Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`. |
| `--remove-api-keys`/`--no-remove-api-keys`| Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`. |
| `--install-completion [bash\|zsh\|fish\|powershell\|pwsh]`| Installs completion for the specified shell. |
| `--show-completion [bash\|zsh\|fish\|powershell\|pwsh]` | Shows completion for the specified shell, allowing you to copy it or customize the installation. |
| `--backend-only` | This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable. For more, see [Backend-only](../deployment/backend-only.md).|
| `--store` | This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable. |
#### Environment Variables
You can configure many of the CLI options using environment variables. These can be exported in your operating system or added to a `.env` file and loaded using the `--env-file` option.
A sample `.env` file named `.env.example` is included with the project. Copy this file to a new file named `.env` and replace the example values with your actual settings. If you're setting values in both your OS and the `.env` file, the `.env` settings will take precedence.
## langflow superuser
Create a superuser for Langflow.
```bash
langflow superuser
# or
python -m langflow superuser
```
### Options
| Option | Type | Description |
|----------------|-------|-------------------------------------------------------------|
| `--username` | TEXT | Username for the superuser. [default: None] [required] |
| `--password` | TEXT | Password for the superuser. [default: None] [required] |
| `--log-level` | TEXT | Logging level. [env var: LANGFLOW_LOG_LEVEL] [default: error] |
| `--help` | | Show this message and exit. |

View file

@ -10,7 +10,7 @@ Langflow [Discord](https://discord.gg/EqksyE2EX9) server.
---
## 🐦 Stay tunned for **Langflow** on Twitter
## 🐦 Stay tuned for **Langflow** on Twitter
Follow [@langflow_ai](https://twitter.com/langflow_ai) on **Twitter** to get the latest news about **Langflow**.

View file

@ -0,0 +1,113 @@
# Backend-only
You can run Langflow in `--backend-only` mode to expose your Langflow app as an API, without running the frontend UI.
Start langflow in backend-only mode with `python3 -m langflow run --backend-only`.
The terminal prints ` Welcome to ⛓ Langflow `, and a blank window opens at `http://127.0.0.1:7864/all`.
Langflow will now serve requests to its API without the frontend running.
## Prerequisites
* [Langflow installed](../getting-started/install-langflow.mdx)
* [OpenAI API key](https://platform.openai.com)
* [A Langflow flow created](../starter-projects/basic-prompting.mdx)
## Download your flow's curl call
1. Click API.
2. Click **curl** > **Copy code** and save the code to your local machine.
It will look something like this:
```curl
curl -X POST \
"http://127.0.0.1:7864/api/v1/run/ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef?stream=false" \
-H 'Content-Type: application/json'\
-d '{"input_value": "message",
"output_type": "chat",
"input_type": "chat",
"tweaks": {
"Prompt-kvo86": {},
"OpenAIModel-MilkD": {},
"ChatOutput-ktwdw": {},
"ChatInput-xXC4F": {}
}}'
```
Note the flow ID of `ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef`. You can find this ID in the UI as well to ensure you're querying the right flow.
## Start Langflow in backend-only mode
1. Stop Langflow with Ctrl+C.
2. Start langflow in backend-only mode with `python3 -m langflow run --backend-only`.
The terminal prints ` Welcome to ⛓ Langflow `, and a blank window opens at `http://127.0.0.1:7864/all`.
Langflow will now serve requests to its API.
3. Run the curl code you copied from the UI.
You should get a result like this:
```bash
{"session_id":"ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef:bf81d898868ac87e1b4edbd96c131c5dee801ea2971122cc91352d144a45b880","outputs":[{"inputs":{"input_value":"hi, are you there?"},"outputs":[{"results":{"result":"Arrr, ahoy matey! Aye, I be here. What be ye needin', me hearty?"},"artifacts":{"message":"Arrr, ahoy matey! Aye, I be here. What be ye needin', me hearty?","sender":"Machine","sender_name":"AI"},"messages":[{"message":"Arrr, ahoy matey! Aye, I be here. What be ye needin', me hearty?","sender":"Machine","sender_name":"AI","component_id":"ChatOutput-ktwdw"}],"component_display_name":"Chat Output","component_id":"ChatOutput-ktwdw","used_frozen_result":false}]}]}%
```
Again, note that the flow ID matches.
Langflow is receiving your POST request, running the flow, and returning the result, all without running the frontend. Cool!
## Download your flow's Python API call
Instead of using curl, you can download your flow as a Python API call instead.
1. Click API.
2. Click **Python API** > **Copy code** and save the code to your local machine.
The code will look something like this:
```python
import requests
from typing import Optional
BASE_API_URL = "http://127.0.0.1:7864/api/v1/run"
FLOW_ID = "ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef"
# You can tweak the flow by adding a tweaks dictionary
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
def run_flow(message: str,
flow_id: str,
output_type: str = "chat",
input_type: str = "chat",
tweaks: Optional[dict] = None,
api_key: Optional[str] = None) -> dict:
"""
Run a flow with a given message and optional tweaks.
:param message: The message to send to the flow
:param flow_id: The ID of the flow to run
:param tweaks: Optional tweaks to customize the flow
:return: The JSON response from the flow
"""
api_url = f"{BASE_API_URL}/{flow_id}"
payload = {
"input_value": message,
"output_type": output_type,
"input_type": input_type,
}
headers = None
if tweaks:
payload["tweaks"] = tweaks
if api_key:
headers = {"x-api-key": api_key}
response = requests.post(api_url, json=payload, headers=headers)
return response.json()
# Setup any tweaks you want to apply to the flow
message = "message"
print(run_flow(message=message, flow_id=FLOW_ID))
```
3. Run your Python app:
```python
python3 app.py
```
The result is similar to the curl call:
```bash
{'session_id': 'ef7e0554-69e5-4e3e-ab29-ee83bcd8d9ef:bf81d898868ac87e1b4edbd96c131c5dee801ea2971122cc91352d144a45b880', 'outputs': [{'inputs': {'input_value': 'message'}, 'outputs': [{'results': {'result': "Arrr matey! What be yer message for this ol' pirate? Speak up or walk the plank!"}, 'artifacts': {'message': "Arrr matey! What be yer message for this ol' pirate? Speak up or walk the plank!", 'sender': 'Machine', 'sender_name': 'AI'}, 'messages': [{'message': "Arrr matey! What be yer message for this ol' pirate? Speak up or walk the plank!", 'sender': 'Machine', 'sender_name': 'AI', 'component_id': 'ChatOutput-ktwdw'}], 'component_display_name': 'Chat Output', 'component_id': 'ChatOutput-ktwdw', 'used_frozen_result': False}]}]}
```
Your Python app POSTs to your Langflow server, and the server runs the flow and returns the result.
See [API](../administration/api.mdx) for more ways to interact with your headless Langflow server.

View file

@ -0,0 +1,65 @@
# Docker
This guide will help you get LangFlow up and running using Docker and Docker Compose.
## Prerequisites
- Docker
- Docker Compose
## Steps
1. Clone the LangFlow repository:
```sh
git clone https://github.com/langflow-ai/langflow.git
```
2. Navigate to the `docker_example` directory:
```sh
cd langflow/docker_example
```
3. Run the Docker Compose file:
```sh
docker compose up
```
LangFlow will now be accessible at [http://localhost:7860/](http://localhost:7860/).
## Docker Compose Configuration
The Docker Compose configuration spins up two services: `langflow` and `postgres`.
### LangFlow Service
The `langflow` service uses the `langflowai/langflow:latest` Docker image and exposes port 7860. It depends on the `postgres` service.
Environment variables:
- `LANGFLOW_DATABASE_URL`: The connection string for the PostgreSQL database.
- `LANGFLOW_CONFIG_DIR`: The directory where LangFlow stores logs, file storage, monitor data, and secret keys.
Volumes:
- `langflow-data`: This volume is mapped to `/var/lib/langflow` in the container.
### PostgreSQL Service
The `postgres` service uses the `postgres:16` Docker image and exposes port 5432.
Environment variables:
- `POSTGRES_USER`: The username for the PostgreSQL database.
- `POSTGRES_PASSWORD`: The password for the PostgreSQL database.
- `POSTGRES_DB`: The name of the PostgreSQL database.
Volumes:
- `langflow-postgres`: This volume is mapped to `/var/lib/postgresql/data` in the container.
## Switching to a Specific LangFlow Version
If you want to use a specific version of LangFlow, you can modify the `image` field under the `langflow` service in the Docker Compose file. For example, to use version 1.0-alpha, change `langflowai/langflow:latest` to `langflowai/langflow:1.0-alpha`.

View file

@ -113,7 +113,10 @@ module.exports = {
type: "category",
label: "Deployment",
collapsed: true,
items: ["deployment/gcp-deployment"],
items: ["deployment/docker",
"deployment/backend-only",
"deployment/gcp-deployment",
],
},
{
type: "category",

View file

@ -1,3 +1,3 @@
<svg width="18" height="18" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 2.36764C23.1181 2.76923 22.1687 3.04081 21.1728 3.16215C22.1898 2.5381 22.9703 1.54857 23.338 0.369812C22.3856 0.947636 21.3334 1.368 20.2092 1.59334C19.3133 0.612492 18.0328 0 16.6156 0C13.8983 0 11.6936 2.26074 11.6936 5.04874C11.6936 5.44456 11.7359 5.82881 11.8204 6.19862C7.72812 5.98771 4.10072 3.97977 1.67071 0.921629C1.24669 1.66992 1.00439 2.5381 1.00439 3.46262C1.00439 5.21343 1.87357 6.75911 3.19493 7.66485C2.38915 7.64029 1.62845 7.41061 0.963547 7.03502V7.09713C0.963547 9.54422 2.66102 11.5854 4.91495 12.0476C4.5022 12.1661 4.06691 12.2253 3.61754 12.2253C3.30058 12.2253 2.99066 12.195 2.69062 12.1358C3.31748 14.1408 5.1347 15.6013 7.29001 15.6403C5.6052 16.9953 3.48089 17.8028 1.17485 17.8028C0.777598 17.8028 0.384575 17.7796 0 17.7334C2.17926 19.1636 4.76844 20 7.54781 20C16.6057 20 21.5573 12.3077 21.5573 5.63524C21.5573 5.41566 21.5531 5.19609 21.5447 4.98084C22.5067 4.26868 23.3422 3.38027 24 2.36764Z" fill="#00AAEC"/>
<svg width="1200" height="1227" viewBox="0 0 1200 1227" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 430 B

Before After
Before After

1100
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow"
version = "1.0.0a43"
version = "1.0.0a49"
description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"]
maintainers = [
@ -66,7 +66,7 @@ qianfan = "0.3.5"
pgvector = "^0.2.3"
pyautogen = "^0.2.0"
langchain-google-genai = "^1.0.1"
langchain-cohere = "^0.1.0rc1"
langchain-cohere = "^0.1.5"
elasticsearch = "^8.12.0"
pytube = "^15.0.0"
dspy-ai = "^2.4.0"
@ -81,7 +81,7 @@ langchain-google-vertexai = "^1.0.3"
langchain-groq = "^0.1.3"
langchain-pinecone = "^0.1.0"
langchain-mistralai = "^0.1.6"
couchbase = "^4.2.1"
couchbase = { extras = ["couchbase"], version = "^4.2.1", optional = true }
youtube-transcript-api = "^0.6.2"
markdown = "^3.6"
langchain-chroma = "^0.1.1"
@ -118,6 +118,7 @@ vulture = "^2.11"
[tool.poetry.extras]
deploy = ["celery", "redis", "flower"]
couchbase = ["couchbase"]
local = ["llama-cpp-python", "sentence-transformers", "ctransformers"]
@ -140,7 +141,7 @@ testpaths = ["tests", "integration"]
console_output_style = "progress"
filterwarnings = ["ignore::DeprecationWarning"]
log_cli = true
markers = ["async_test"]
markers = ["async_test", "api_key_required"]
[tool.ruff]

View file

@ -1,27 +0,0 @@
from .AstraDBSearch import AstraDBSearchComponent
from .ChromaSearch import ChromaSearchComponent
from .FAISSSearch import FAISSSearchComponent
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
from .PineconeSearch import PineconeSearchComponent
from .QdrantSearch import QdrantSearchComponent
from .RedisSearch import RedisSearchComponent
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
from .VectaraSearch import VectaraSearchComponent
from .WeaviateSearch import WeaviateSearchVectorStore
from .pgvectorSearch import PGVectorSearchComponent
from .Couchbase import CouchbaseSearchComponent # type: ignore
__all__ = [
"AstraDBSearchComponent",
"ChromaSearchComponent",
"CouchbaseSearchComponent",
"FAISSSearchComponent",
"MongoDBAtlasSearchComponent",
"PineconeSearchComponent",
"QdrantSearchComponent",
"RedisSearchComponent",
"SupabaseSearchComponent",
"VectaraSearchComponent",
"WeaviateSearchVectorStore",
"PGVectorSearchComponent",
]

View file

@ -1,28 +0,0 @@
from .AstraDB import AstraDBVectorStoreComponent
from .Chroma import ChromaComponent
from .FAISS import FAISSComponent
from .MongoDBAtlasVector import MongoDBAtlasComponent
from .Pinecone import PineconeComponent
from .Qdrant import QdrantComponent
from .Redis import RedisComponent
from .SupabaseVectorStore import SupabaseComponent
from .Vectara import VectaraComponent
from .Weaviate import WeaviateVectorStoreComponent
from .pgvector import PGVectorComponent
from .Couchbase import CouchbaseComponent
__all__ = [
"AstraDBVectorStoreComponent",
"ChromaComponent",
"CouchbaseComponent",
"FAISSComponent",
"MongoDBAtlasComponent",
"PineconeComponent",
"QdrantComponent",
"RedisComponent",
"SupabaseComponent",
"VectaraComponent",
"WeaviateVectorStoreComponent",
"base",
"PGVectorComponent",
]

View file

@ -297,7 +297,7 @@ class CodeParser:
bases = self.execute_and_inspect_classes(self.code)
except Exception as e:
# If the code cannot be executed, return an empty list
logger.exception(e)
logger.debug(e)
bases = []
raise e
return bases

View file

@ -78,7 +78,8 @@ class DirectoryReader:
component_tuple = (*build_component(component), component)
components.append(component_tuple)
except Exception as e:
logger.error(f"Error while loading component { component['name']}: {e}")
logger.debug(f"Error while loading component { component['name']}")
logger.debug(e)
continue
items.append({"name": menu["name"], "path": menu["path"], "components": components})
filtered = [menu for menu in items if menu["components"]]
@ -266,8 +267,7 @@ class DirectoryReader:
if validation_result:
try:
output_types = self.get_output_types_from_code(result_content)
except Exception as exc:
logger.exception(f"Error while getting output types from code: {str(exc)}")
except Exception:
output_types = [component_name_camelcase]
else:
output_types = [component_name_camelcase]

View file

@ -251,7 +251,7 @@ def get_flow_by_id_or_endpoint_name(
flow = db.get(Flow, flow_id)
except ValueError:
endpoint_name = flow_id_or_name
stmt = select(Flow).where(Flow.name == endpoint_name)
stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)
if user_id:
stmt = stmt.where(Flow.user_id == user_id)
flow = db.exec(stmt).first()

View file

@ -1159,13 +1159,13 @@ files = [
[[package]]
name = "langchain"
version = "0.2.2"
version = "0.2.3"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langchain-0.2.2-py3-none-any.whl", hash = "sha256:58ca0c47bcdd156da66f50a0a4fcedc49bf6950827f4a6b06c8c4842d55805f3"},
{file = "langchain-0.2.2.tar.gz", hash = "sha256:9d61e50e9cdc2bea659bc5e6c03650ba048fda63a307490ae368e539f61a0d3a"},
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"},
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"},
]
[package.dependencies]
@ -1181,29 +1181,15 @@ requests = ">=2,<3"
SQLAlchemy = ">=1.4,<3"
tenacity = ">=8.1.0,<9.0.0"
[package.extras]
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
clarifai = ["clarifai (>=9.1.0)"]
cli = ["typer (>=0.9.0,<0.10.0)"]
cohere = ["cohere (>=4,<6)"]
docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"]
embeddings = ["sentence-transformers (>=2,<3)"]
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
javascript = ["esprima (>=4.0.1,<5.0.0)"]
llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"]
openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"]
qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"]
text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
[[package]]
name = "langchain-community"
version = "0.2.3"
version = "0.2.4"
description = "Community contributed LangChain integrations."
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langchain_community-0.2.3-py3-none-any.whl", hash = "sha256:aa895545be2f3f4aa2fea36f6da2e3b4ec50ce61ec986e8f146901a1e9138138"},
{file = "langchain_community-0.2.3.tar.gz", hash = "sha256:a3c35af215e47b700e7cb4e548fa8b45c6d46d52b5a5a65af2577c5a0104fc9f"},
{file = "langchain_community-0.2.4-py3-none-any.whl", hash = "sha256:8582e9800f4837660dc297cccd2ee1ddc1d8c440d0fe8b64edb07620f0373b0e"},
{file = "langchain_community-0.2.4.tar.gz", hash = "sha256:2bb6a1a36b8500a564d25d76469c02457b1a7c3afea6d4a609a47c06b993e3e4"},
]
[package.dependencies]
@ -1218,19 +1204,15 @@ requests = ">=2,<3"
SQLAlchemy = ">=1.4,<3"
tenacity = ">=8.1.0,<9.0.0"
[package.extras]
cli = ["typer (>=0.9.0,<0.10.0)"]
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "azure-identity (>=1.15.0,<2.0.0)", "azure-search-documents (==11.4.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.6,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpathlib (>=0.18,<0.19)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "httpx-sse (>=0.4.0,<0.5.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "oracledb (>=2.2.0,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "premai (>=0.3.25,<0.4.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pyjwt (>=2.8.0,<3.0.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "simsimd (>=4.3.1,<5.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "vdms (>=0.0.20,<0.0.21)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
[[package]]
name = "langchain-core"
version = "0.2.4"
version = "0.2.5"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langchain_core-0.2.4-py3-none-any.whl", hash = "sha256:5212f7ec78a525e88a178ed3aefe2fd7134b03fb92573dfbab9914f1d92d6ec5"},
{file = "langchain_core-0.2.4.tar.gz", hash = "sha256:82bdcc546eb0341cefcf1f4ecb3e49836fff003903afddda2d1312bb8491ef81"},
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"},
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"},
]
[package.dependencies]
@ -1241,9 +1223,6 @@ pydantic = ">=1,<3"
PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0"
[package.extras]
extended-testing = ["jinja2 (>=3,<4)"]
[[package]]
name = "langchain-experimental"
version = "0.0.60"
@ -1296,13 +1275,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0"
[[package]]
name = "langsmith"
version = "0.1.72"
version = "0.1.75"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langsmith-0.1.72-py3-none-any.whl", hash = "sha256:a4456707669521bd75b7431b9205a6b99579fb9ff01bd338f52d29df11a7662d"},
{file = "langsmith-0.1.72.tar.gz", hash = "sha256:262ae9e8aceaba50f3a0f5b6eb559d6110886f0afc6b0ed5270e7d3d3f1fd8d6"},
{file = "langsmith-0.1.75-py3-none-any.whl", hash = "sha256:d08b08dd6b3fa4da170377f95123d77122ef4c52999d10fff4ae08ff70d07aed"},
{file = "langsmith-0.1.75.tar.gz", hash = "sha256:61274e144ea94c297dd78ce03e6dfae18459fe9bd8ab5094d61a0c4816561279"},
]
[package.dependencies]
@ -1600,13 +1579,13 @@ files = [
[[package]]
name = "marshmallow"
version = "3.21.2"
version = "3.21.3"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false
python-versions = ">=3.8"
files = [
{file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"},
{file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"},
{file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"},
{file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"},
]
[package.dependencies]

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow-base"
version = "0.0.57"
version = "0.0.60"
description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"]
maintainers = [

View file

@ -41,7 +41,6 @@
"class-variance-authority": "^0.6.1",
"clsx": "^1.2.1",
"cmdk": "^1.0.0",
"debounce-promise": "^3.1.2",
"dompurify": "^3.0.5",
"dotenv": "^16.4.5",
"emoji-regex": "^10.3.0",
@ -53,6 +52,7 @@
"million": "^3.0.6",
"moment": "^2.29.4",
"openseadragon": "^4.1.1",
"p-debounce": "^4.0.0",
"playwright": "^1.42.0",
"react": "^18.2.21",
"react-ace": "^10.1.0",
@ -480,7 +480,6 @@
},
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
"version": "1.3.0",
"extraneous": true,
"inBundle": true,
"license": "MIT",
"engines": {
@ -5699,11 +5698,6 @@
"node": ">=12"
}
},
"node_modules/debounce-promise": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz",
"integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -10051,6 +10045,15 @@
"node": ">=8"
}
},
"node_modules/p-debounce": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-4.0.0.tgz",
"integrity": "sha512-4Ispi9I9qYGO4lueiLDhe4q4iK5ERK8reLsuzH6BPaXn53EGaua8H66PXIFGrW897hwjXp+pVLrm/DLxN0RF0A==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",

View file

@ -36,7 +36,6 @@
"class-variance-authority": "^0.6.1",
"clsx": "^1.2.1",
"cmdk": "^1.0.0",
"debounce-promise": "^3.1.2",
"dompurify": "^3.0.5",
"dotenv": "^16.4.5",
"emoji-regex": "^10.3.0",
@ -48,6 +47,7 @@
"million": "^3.0.6",
"moment": "^2.29.4",
"openseadragon": "^4.1.1",
"p-debounce": "^4.0.0",
"playwright": "^1.42.0",
"react": "^18.2.21",
"react-ace": "^10.1.0",

View file

@ -1,5 +1,5 @@
import { cloneDeep } from "lodash";
import emojiRegex from "emoji-regex";
import { cloneDeep } from "lodash";
import { useCallback, useEffect, useMemo, useState } from "react";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import IconComponent from "../../components/genericIconComponent";
@ -56,14 +56,14 @@ export default function GenericNode({
const [nodeName, setNodeName] = useState(data.node!.display_name);
const [inputDescription, setInputDescription] = useState(false);
const [nodeDescription, setNodeDescription] = useState(
data.node?.description!
data.node?.description!,
);
const [isOutdated, setIsOutdated] = useState(false);
const buildStatus = useFlowStore(
(state) => state.flowBuildStatus[data.id]?.status
(state) => state.flowBuildStatus[data.id]?.status,
);
const lastRunTime = useFlowStore(
(state) => state.flowBuildStatus[data.id]?.timestamp
(state) => state.flowBuildStatus[data.id]?.timestamp,
);
const [validationStatus, setValidationStatus] =
useState<validationStatusType | null>(null);
@ -116,7 +116,7 @@ export default function GenericNode({
updateNodeInternals(data.id);
},
[data.id, data.node, setNode, setIsOutdated]
[data.id, data.node, setNode, setIsOutdated],
);
if (!data.node!.template) {
@ -255,8 +255,17 @@ export default function GenericNode({
const isDark = useDarkStore((state) => state.dark);
const renderIconStatus = (
buildStatus: BuildStatus | undefined,
validationStatus: validationStatusType | null
validationStatus: validationStatusType | null,
) => {
const conditionSuccess = validationStatus && validationStatus.valid;
const conditionInactive =
validationStatus &&
!validationStatus.valid &&
buildStatus === BuildStatus.INACTIVE;
const conditionError =
buildStatus === BuildStatus.ERROR ||
(validationStatus && !validationStatus.valid);
if (buildStatus === BuildStatus.BUILDING) {
return <Loading className="text-medium-indigo" />;
} else {
@ -264,31 +273,30 @@ export default function GenericNode({
<>
<IconComponent
name="Play"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
className={cn(
!conditionSuccess && !conditionInactive && !conditionError
? "opacity-100"
: "opacity-0",
"absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground transition-all group-hover:text-medium-indigo group-hover/node:opacity-100",
)}
/>
{validationStatus && validationStatus.valid ? (
{conditionSuccess ? (
<Checkmark
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover/node:opacity-0"
isVisible={true}
/>
) : validationStatus &&
!validationStatus.valid &&
buildStatus === BuildStatus.INACTIVE ? (
) : conditionInactive ? (
<IconComponent
name="Play"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-gray opacity-30 transition-all group-hover/node:opacity-0"
/>
) : buildStatus === BuildStatus.ERROR ||
(validationStatus && !validationStatus.valid) ? (
) : conditionError ? (
<Xmark
isVisible={true}
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover/node:opacity-0"
/>
) : (
<IconComponent
name="Play"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
/>
<></>
)}
</>
);
@ -296,7 +304,7 @@ export default function GenericNode({
};
const getSpecificClassFromBuildStatus = (
buildStatus: BuildStatus | undefined,
validationStatus: validationStatusType | null
validationStatus: validationStatusType | null,
) => {
let isInvalid = validationStatus && !validationStatus.valid;
@ -320,11 +328,11 @@ export default function GenericNode({
selected: boolean,
showNode: boolean,
buildStatus: BuildStatus | undefined,
validationStatus: validationStatusType | null
validationStatus: validationStatusType | null,
) => {
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
buildStatus,
validationStatus
validationStatus,
);
const baseBorderClass = getBaseBorderClass(selected);
@ -332,8 +340,8 @@ export default function GenericNode({
const names = classNames(
baseBorderClass,
nodeSizeClass,
"generic-node-div",
specificClassFromBuildStatus
"generic-node-div group/node",
specificClassFromBuildStatus,
);
return names;
};
@ -393,7 +401,7 @@ export default function GenericNode({
selected,
showNode,
buildStatus,
validationStatus
validationStatus,
)}
>
{data.node?.beta && showNode && (
@ -524,7 +532,7 @@ export default function GenericNode({
}
title={getFieldTitle(
data.node?.template!,
templateField
templateField,
)}
info={data.node?.template[templateField].info}
name={templateField}
@ -552,7 +560,7 @@ export default function GenericNode({
proxy={data.node?.template[templateField].proxy}
showNode={showNode}
/>
)
),
)}
<ParameterComponent
key={scapedJSONStringfy({
@ -709,7 +717,7 @@ export default function GenericNode({
!data.node?.description) &&
nameEditable
? "font-light italic"
: ""
: "",
)}
onClick={(e) => {
setInputDescription(true);
@ -771,13 +779,13 @@ export default function GenericNode({
}
title={getFieldTitle(
data.node?.template!,
templateField
templateField,
)}
info={data.node?.template[templateField].info}
name={templateField}
tooltipTitle={
data.node?.template[templateField].input_types?.join(
"\n"
"\n",
) ?? data.node?.template[templateField].type
}
required={data.node!.template[templateField].required}
@ -804,7 +812,7 @@ export default function GenericNode({
<div
className={classNames(
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
"flex-max-width justify-center"
"flex-max-width justify-center",
)}
>
{" "}

View file

@ -51,13 +51,15 @@ export default function ErrorAlert({
/>
</div>
<div className="ml-3">
<h3 className="error-build-foreground">{title}</h3>
<h3 className="error-build-foreground line-clamp-2">{title}</h3>
{list?.length !== 0 &&
list?.some((item) => item !== null && item !== undefined) ? (
<div className="error-build-message-div">
<ul className="error-build-message-list">
{list.map((item, index) => (
<li key={index}>{item}</li>
<li key={index} className="line-clamp-5">
{item}
</li>
))}
</ul>
</div>

View file

@ -47,7 +47,7 @@ export default function NoticeAlert({
/>
</div>
<div className="ml-3 flex-1 md:flex md:justify-between">
<p className="text-sm text-info-foreground word-break-break-word">
<p className="line-clamp-2 text-sm text-info-foreground word-break-break-word">
{title}
</p>
<p className="mt-3 text-sm md:ml-6 md:mt-0">

View file

@ -45,7 +45,7 @@ export default function SuccessAlert({
/>
</div>
<div className="ml-3">
<p className="success-alert-message">{title}</p>
<p className="success-alert-message line-clamp-2">{title}</p>
</div>
</div>
</div>

View file

@ -56,7 +56,7 @@ export default function Header(): JSX.Element {
const lastFlowVisitedIndex = routeHistory
.reverse()
.findIndex(
(path) => path.includes("/flow/") && path !== location.pathname
(path) => path.includes("/flow/") && path !== location.pathname,
);
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
@ -81,14 +81,16 @@ export default function Header(): JSX.Element {
<span className="ml-4 text-2xl"></span>
</Link>
{showArrowReturnIcon && (
<button
<Button
variant="none"
size="none"
onClick={() => {
checkForChanges();
redirectToLastLocation();
}}
>
<IconComponent name="ChevronLeft" className="w-4" />
</button>
</Button>
)}
<MenuBar />
@ -186,7 +188,9 @@ export default function Header(): JSX.Element {
<Separator orientation="vertical" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
<Button
variant="none"
size="none"
data-testid="user-profile-settings"
className={
"h-7 w-7 rounded-full focus-visible:outline-0 " +
@ -200,6 +204,28 @@ export default function Header(): JSX.Element {
/>
</DropdownMenuTrigger>
<DropdownMenuContent>
{!autoLogin && (
<>
<DropdownMenuLabel>
<div className="flex items-center gap-3">
<div
className={
"h-5 w-5 rounded-full focus-visible:outline-0 " +
(userData?.profile_image ??
(userData?.id
? gradients[
parseInt(userData?.id ?? "", 30) %
gradients.length
]
: "bg-gray-500"))
}
/>
{userData?.username ?? "User"}
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
</>
)}
<DropdownMenuLabel>General</DropdownMenuLabel>
<DropdownMenuItem
className="cursor-pointer"

View file

@ -13,6 +13,7 @@ import IconComponent, {
import { Button, buttonVariants } from "../../../ui/button";
import { Input } from "../../../ui/input";
import useFileDrop from "../../hooks/use-on-file-drop";
import useAlertStore from "../../../../stores/alertStore";
type SideBarFoldersButtonsComponentProps = {
folders: FolderType[];
@ -33,7 +34,7 @@ const SideBarFoldersButtonsComponent = ({
const [foldersNames, setFoldersNames] = useState({});
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const [editFolders, setEditFolderName] = useState(
folders.map((obj) => ({ name: obj.name, edit: false }))
folders.map((obj) => ({ name: obj.name, edit: false })),
);
const uploadFolder = useFolderStore((state) => state.uploadFolder);
const currentFolder = pathname.split("/");
@ -51,6 +52,7 @@ const SideBarFoldersButtonsComponent = ({
const location = useLocation();
const folderId = location?.state?.folderId ?? myCollectionId;
const getFolderById = useFolderStore((state) => state.getFolderById);
const setErrorData = useAlertStore((state) => state.setErrorData);
const handleFolderChange = (folderId: string) => {
getFolderById(folderId);
@ -58,11 +60,21 @@ const SideBarFoldersButtonsComponent = ({
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
folderId,
handleFolderChange
handleFolderChange,
);
const handleUploadFlowsToFolder = () => {
uploadFolder(folderId);
uploadFolder(folderId)
.then(() => {
getFolderById(folderId);
})
.catch((err) => {
console.log(err);
setErrorData({
title: `Error on upload`,
list: [err["response"]["data"]],
});
});
};
const handleDownloadFolder = (id: string) => {
@ -73,7 +85,7 @@ const SideBarFoldersButtonsComponent = ({
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
(res) => {
getFoldersApi(true);
}
},
);
}
@ -119,7 +131,7 @@ const SideBarFoldersButtonsComponent = ({
<>
{folders.map((item, index) => {
const editFolderName = editFolders?.filter(
(folder) => folder.name === item.name
(folder) => folder.name === item.name,
)[0];
return (
<div
@ -135,7 +147,7 @@ const SideBarFoldersButtonsComponent = ({
? "border border-border bg-muted hover:bg-muted"
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
folderIdDragging === item.id! ? "bg-border" : ""
folderIdDragging === item.id! ? "bg-border" : "",
)}
onClick={() => handleChangeFolder!(item.id!)}
>
@ -205,7 +217,7 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({
name: obj.name,
edit: false,
}))
})),
);
}
if (e.key === "Enter") {
@ -238,10 +250,10 @@ const SideBarFoldersButtonsComponent = ({
};
const updatedFolder = await updateFolder(
body,
item.id!
item.id!,
);
const updateFolders = folders.filter(
(f) => f.name !== item.name
(f) => f.name !== item.name,
);
setFolders([...updateFolders, updatedFolder]);
setFoldersNames({});
@ -249,7 +261,7 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({
name: obj.name,
edit: false,
}))
})),
);
} else {
setFoldersNames((old) => ({

View file

@ -12,7 +12,6 @@ type SidebarNavProps = {
title: string;
icon: React.ReactNode;
}[];
handleOpenNewFolderModal?: () => void;
handleChangeFolder?: (id: string) => void;
handleEditFolder?: (item: FolderType) => void;
handleDeleteFolder?: (item: FolderType) => void;

View file

@ -13,6 +13,7 @@ import ForwardedIconComponent from "../genericIconComponent";
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
import ResetColumns from "./components/ResetColumns";
import resetGrid from "./utils/reset-grid-columns";
import { useParams } from "react-router-dom";
interface TableComponentProps extends AgGridReactProps {
columnDefs: NonNullable<AgGridReactProps["columnDefs"]>;
@ -32,7 +33,7 @@ const TableComponent = forwardRef<
alertDescription = DEFAULT_TABLE_ALERT_MSG,
...props
},
ref
ref,
) => {
let colDef = props.columnDefs.map((col, index) => {
let newCol = {
@ -82,16 +83,16 @@ const TableComponent = forwardRef<
// @ts-ignore
realRef.current = params;
const updatedColumnDefs = makeLastColumnNonResizable([...colDef]);
params.api.setColumnDefs(updatedColumnDefs);
params.api.setGridOption("columnDefs", updatedColumnDefs);
initialColumnDefs.current = params.api.getColumnDefs();
if (props.onGridReady) props.onGridReady(params);
};
const onColumnMoved = (params) => {
const updatedColumnDefs = makeLastColumnNonResizable(
params.columnApi.getAllGridColumns().map((col) => col.getColDef())
params.columnApi.getAllGridColumns().map((col) => col.getColDef()),
);
params.api.setColumnDefs(updatedColumnDefs);
params.api.setGridOption("columnDefs", updatedColumnDefs);
if (props.onColumnMoved) props.onColumnMoved(params);
};
@ -114,7 +115,7 @@ const TableComponent = forwardRef<
className={cn(
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
"ag-theme-shadcn flex h-full flex-col",
"relative"
"relative",
)} // applying the grid theme
>
<AgGridReact
@ -125,9 +126,6 @@ const TableComponent = forwardRef<
}}
columnDefs={colDef}
ref={realRef}
getRowId={(params) => {
return params.data.id;
}}
pagination={true}
onGridReady={onGridReady}
onColumnMoved={onColumnMoved}
@ -135,7 +133,7 @@ const TableComponent = forwardRef<
<ResetColumns resetGrid={() => resetGrid(realRef, initialColumnDefs)} />
</div>
);
}
},
);
export default TableComponent;

View file

@ -668,7 +668,7 @@ export const ZERO_NOTIFICATIONS = "No new notifications";
export const SUCCESS_BUILD = "Built sucessfully ✨";
export const ALERT_SAVE_WITH_API =
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
"Caution: Unchecking this box only removes API keys from fields specifically designated for API keys.";
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";

View file

@ -6,9 +6,9 @@ const SvgBotMessageSquare = (props) => (
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="lucide lucide-bot-message-square"
{...props}
>

View file

@ -133,7 +133,7 @@ export default function ChatView({
function updateChat(
chat: ChatMessageType,
message: string,
stream_url?: string
stream_url?: string,
) {
// if (message === "") return;
chat.message = message;
@ -165,14 +165,12 @@ export default function ChatView({
className="flex gap-1"
size="none"
variant="none"
disabled={lockChat}
onClick={() => handleSelectChange("builds")}
>
<IconComponent
name="Eraser"
className={classNames(
"h-5 w-5 transition-all duration-100",
lockChat ? "animate-pulse text-primary" : "text-primary"
)}
className={classNames("h-5 w-5 text-primary")}
aria-hidden="true"
/>
</Button>

View file

@ -44,17 +44,17 @@ export const switchCaseModalSize = (size: string) => {
break;
case "large-thin":
minWidth = "min-w-[65vw]";
height = "h-[80vh]";
height = "h-[90vh]";
break;
case "md-thin":
minWidth = "min-w-[85vw]";
height = "h-[70vh]";
height = "h-[90vh]";
break;
case "sm-thin":
minWidth = "min-w-[65vw]";
height = "h-[70vh]";
height = "h-[90vh]";
break;
case "large-h-full":

View file

@ -103,7 +103,7 @@ interface BaseModalProps {
React.ReactElement<ContentProps>,
React.ReactElement<HeaderProps>,
React.ReactElement<TriggerProps>?,
React.ReactElement<FooterProps>?
React.ReactElement<FooterProps>?,
];
open?: boolean;
setOpen?: (open: boolean) => void;
@ -138,16 +138,16 @@ function BaseModal({
onSubmit,
}: BaseModalProps) {
const headerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Header
(child) => (child as React.ReactElement).type === Header,
);
const triggerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Trigger
(child) => (child as React.ReactElement).type === Trigger,
);
const ContentChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Content
(child) => (child as React.ReactElement).type === Content,
);
const ContentFooter = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Footer
(child) => (child as React.ReactElement).type === Footer,
);
let { minWidth, height } = switchCaseModalSize(size);
@ -164,17 +164,21 @@ function BaseModal({
{type === "modal" ? (
<Modal open={open} onOpenChange={setOpen}>
{triggerChild}
<ModalContent className={cn(minWidth, "duration-300")}>
<div className="truncate-doubleline word-break-break-word">
<ModalContent
className={cn(minWidth, height, "flex flex-col duration-300")}
>
<div className="flex-shrink-0 truncate-doubleline word-break-break-word">
{headerChild}
</div>
<div
className={`flex flex-col ${height} w-full transition-all duration-300`}
className={`flex w-full flex-1 flex-col transition-all duration-300`}
>
{ContentChild}
</div>
{ContentFooter && (
<div className="flex flex-row-reverse">{ContentFooter}</div>
<div className="flex flex-shrink-0 flex-row-reverse">
{ContentFooter}
</div>
)}
</ModalContent>
</Modal>
@ -184,7 +188,7 @@ function BaseModal({
<DialogContent
className={cn(minWidth, height, "flex flex-col duration-300")}
>
<div className="truncate-doubleline word-break-break-word">
<div className="flex-shrink-0 truncate-doubleline word-break-break-word">
{headerChild}
</div>
{onSubmit ? (
@ -193,26 +197,30 @@ function BaseModal({
event.preventDefault();
onSubmit();
}}
className="flex flex-1 flex-col gap-6"
className="flex min-h-0 flex-1 flex-col gap-6"
>
<div
className={`flex w-full flex-1 flex-col transition-all duration-300`}
className={`flex w-full flex-1 flex-col overflow-hidden transition-all duration-300`}
>
{ContentChild}
</div>
{ContentFooter && (
<div className="flex flex-row-reverse">{ContentFooter}</div>
<div className="flex flex-shrink-0 flex-row-reverse">
{ContentFooter}
</div>
)}
</Form.Root>
) : (
<>
<div
className={`flex w-full flex-1 flex-col transition-all duration-300`}
className={`flex min-h-0 w-full flex-1 flex-col transition-all duration-300`}
>
{ContentChild}
</div>
{ContentFooter && (
<div className="flex flex-row-reverse">{ContentFooter}</div>
<div className="flex flex-shrink-0 flex-row-reverse">
{ContentFooter}
</div>
)}
</>
)}

View file

@ -45,7 +45,7 @@ const ExportModal = forwardRef(
is_component: false,
},
name!,
description
description,
);
setNoticeData({
title: API_WARNING_NOTICE_ALERT,
@ -61,7 +61,7 @@ const ExportModal = forwardRef(
is_component: false,
}),
name!,
description
description,
);
setOpen(false);
}}
@ -94,7 +94,7 @@ const ExportModal = forwardRef(
{SAVE_WITH_API_CHECKBOX}
</label>
</div>
<span className=" text-xs text-destructive ">
<span className="mt-1 text-xs text-destructive ">
{ALERT_SAVE_WITH_API}
</span>
</BaseModal.Content>
@ -102,6 +102,6 @@ const ExportModal = forwardRef(
<BaseModal.Footer submit={{ label: "Download Flow" }} />
</BaseModal>
);
}
},
);
export default ExportModal;

View file

@ -11,13 +11,13 @@ import ReactFlow, {
SelectionDragHandler,
updateEdge,
} from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode";
import {
INVALID_SELECTION_ERROR_ALERT,
UPLOAD_ALERT_LIST,
UPLOAD_ERROR_ALERT,
WRONG_FILE_ERROR_ALERT,
} from "../../../../constants/alerts_constants";
import GenericNode from "../../../../customNodes/genericNode";
import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";

View file

@ -4,7 +4,9 @@ import { FormProvider, useForm, useWatch } from "react-hook-form";
import { Link, useLocation, useNavigate } from "react-router-dom";
import CollectionCardComponent from "../../../../components/cardComponent";
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
import IconComponent from "../../../../components/genericIconComponent";
import IconComponent, {
ForwardedIconComponent,
} from "../../../../components/genericIconComponent";
import PaginatorComponent from "../../../../components/paginatorComponent";
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
import { Button } from "../../../../components/ui/button";
@ -18,6 +20,9 @@ import { getNameByType } from "../../utils/get-name-by-type";
import { sortFlows } from "../../utils/sort-flows";
import EmptyComponent from "../emptyComponent";
import HeaderComponent from "../headerComponent";
import { downloadFlow, removeApiKeys } from "../../../../utils/reactflowUtils";
import { useDarkStore } from "../../../../stores/darkStore";
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
export default function ComponentsComponent({
type = "all",
@ -31,22 +36,22 @@ export default function ComponentsComponent({
const allFlows = useFlowsManagerStore((state) => state.allFlows);
const flowsFromFolder = useFolderStore(
(state) => state.selectedFolder?.flows
(state) => state.selectedFolder?.flows,
);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const [openDelete, setOpenDelete] = useState(false);
const searchFlowsComponents = useFlowsManagerStore(
(state) => state.searchFlowsComponents
(state) => state.searchFlowsComponents,
);
const setSelectedFlowsComponentsCards = useFlowsManagerStore(
(state) => state.setSelectedFlowsComponentsCards
(state) => state.setSelectedFlowsComponentsCards,
);
const selectedFlowsComponentsCards = useFlowsManagerStore(
(state) => state.selectedFlowsComponentsCards
(state) => state.selectedFlowsComponentsCards,
);
const [handleFileDrop] = useFileDrop(uploadFlow, type)!;
@ -82,7 +87,7 @@ export default function ComponentsComponent({
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
f.description
.toLowerCase()
.includes(searchFlowsComponents.toLowerCase())
.includes(searchFlowsComponents.toLowerCase()),
);
if (searchFlowsComponents === "") {
@ -129,6 +134,8 @@ export default function ComponentsComponent({
setOpenDelete(true);
} else if (action === "duplicate") {
handleDuplicate();
} else if (action === "export") {
handleExport();
}
};
@ -137,9 +144,9 @@ export default function ComponentsComponent({
selectedFlowsComponentsCards.map((selectedFlow) =>
addFlow(
true,
allFlows.find((flow) => flow.id === selectedFlow)
)
)
allFlows.find((flow) => flow.id === selectedFlow),
),
),
).then(() => {
resetFilter();
getFoldersApi(true);
@ -152,6 +159,47 @@ export default function ComponentsComponent({
});
};
const handleImport = () => {
uploadFlow({ newProject: true, isComponent: false })
.then(() => {
resetFilter();
getFoldersApi(true);
if (!folderId || folderId === myCollectionId) {
getFolderById(folderId ? folderId : myCollectionId);
}
setSelectedFlowsComponentsCards([]);
setSuccessData({ title: "Flows imported successfully" });
})
.catch((error) => {
setErrorData({
title: UPLOAD_ERROR_ALERT,
list: [error],
});
});
};
const version = useDarkStore((state) => state.version);
const handleExport = () => {
selectedFlowsComponentsCards.map((selectedFlowId) => {
const selectedFlow = allFlows.find((flow) => flow.id === selectedFlowId);
downloadFlow(
removeApiKeys({
id: selectedFlow!.id,
data: selectedFlow!.data!,
description: selectedFlow!.description,
name: selectedFlow!.name,
last_tested_version: version,
is_component: false,
}),
selectedFlow!.name,
selectedFlow!.description,
);
});
setSuccessData({ title: "Flows exported successfully" });
};
const handleDeleteMultiple = () => {
removeFlow(selectedFlowsComponentsCards)
.then(() => {
@ -161,7 +209,7 @@ export default function ComponentsComponent({
getFolderById(folderId ? folderId : myCollectionId);
}
setSuccessData({
title: "Selected items deleted successfully!",
title: "Selected items deleted successfully",
});
})
.catch(() => {
@ -180,7 +228,7 @@ export default function ComponentsComponent({
return true;
}
return false;
}
},
);
setSelectedFlowsComponentsCards(selectedFlows);
@ -215,20 +263,23 @@ export default function ComponentsComponent({
if (type === "all") return allFlows?.length;
return allFlows?.filter(
(f) => (f.is_component ?? false) === (type === "component")
(f) => (f.is_component ?? false) === (type === "component"),
)?.length;
};
return (
<>
{allFlows?.length > 0 && (
<HeaderComponent
handleDelete={() => handleSelectOptionsChange("delete")}
handleSelectAll={handleSelectAll}
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
/>
)}
<div className="flex w-full gap-4 pb-5">
{allFlows?.length > 0 && (
<HeaderComponent
handleDelete={() => handleSelectOptionsChange("delete")}
handleSelectAll={handleSelectAll}
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
handleExport={() => handleSelectOptionsChange("export")}
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
/>
)}
</div>
<CardsWrapComponent
onFileDrop={handleFileDrop}

View file

@ -13,7 +13,7 @@ const EmptyComponent = ({}: EmptyComponentProps) => {
return (
<>
<div className="mt-6 flex w-full items-center justify-center text-center">
<div className="mt-2 flex w-full items-center justify-center text-center">
<div className="flex-max-width h-full flex-col">
<div className="align-center flex w-full justify-center gap-1 ">
<span className="text-muted-foreground">

View file

@ -1,13 +1,17 @@
import { useState } from "react";
import IconComponent from "../../../../components/genericIconComponent";
import IconComponent, {
ForwardedIconComponent,
} from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import { Checkbox } from "../../../../components/ui/checkbox";
import { cn } from "../../../../utils/utils";
import { Button } from "../../../../components/ui/button";
type HeaderComponentProps = {
handleSelectAll: (select) => void;
handleDelete: () => void;
handleDuplicate: () => void;
handleExport: () => void;
disableFunctions: boolean;
};
@ -15,6 +19,7 @@ const HeaderComponent = ({
handleSelectAll,
handleDelete,
handleDuplicate,
handleExport,
disableFunctions,
}: HeaderComponentProps) => {
const [shouldSelectAll, setShouldSelectAll] = useState(true);
@ -26,29 +31,41 @@ const HeaderComponent = ({
return (
<>
<div className="grid grid-cols-3 pb-5">
<div className="col-auto grid-cols-1 self-center justify-self-start">
<a onClick={handleClick} className="text-sm">
<div className="header-menu-bar-display ">
<div
className="header-menu-flow-name"
data-testid="select_all_collection"
>
<div className="flex items-center space-x-2">
<Checkbox checked={!shouldSelectAll} id="terms" />
<label
onClick={handleClick}
htmlFor="terms"
className="label cursor-pointer text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{shouldSelectAll ? "Select All" : "Unselect All"}
</label>
</div>
</div>
<div className="flex w-full items-center justify-between gap-4">
<div className="flex items-center justify-self-start">
<a onClick={handleClick} className="cursor-pointer text-sm">
<div className="flex items-center space-x-2">
<Checkbox checked={!shouldSelectAll} id="terms" />
<span className="label text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{shouldSelectAll ? "Select All" : "Unselect All"}
</span>
</div>
</a>
</div>
<div className="col-span-2 flex grid-cols-1 gap-2 justify-self-end">
<div className="flex items-center gap-2">
<div>
<ShadTooltip
content={
disableFunctions ? (
<span>Select items to export</span>
) : (
<span>Export selected items</span>
)
}
>
<Button
variant="none"
size="none"
onClick={handleExport}
disabled={disableFunctions}
>
<IconComponent
name="FileDown"
className={cn("h-5 w-5 text-primary transition-all")}
/>
</Button>
</ShadTooltip>
</div>
<div>
<ShadTooltip
content={
@ -59,12 +76,17 @@ const HeaderComponent = ({
)
}
>
<button onClick={handleDuplicate} disabled={disableFunctions}>
<Button
variant="none"
size="none"
onClick={handleDuplicate}
disabled={disableFunctions}
>
<IconComponent
name="Copy"
className={cn("h-5 w-5 text-primary transition-all")}
/>
</button>
</Button>
</ShadTooltip>
</div>
<div>
@ -77,15 +99,20 @@ const HeaderComponent = ({
)
}
>
<button onClick={handleDelete} disabled={disableFunctions}>
<Button
variant="none"
size="none"
onClick={handleDelete}
disabled={disableFunctions}
>
<IconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-primary transition-all",
disableFunctions ? "" : "hover:text-destructive"
disableFunctions ? "" : "hover:text-destructive",
)}
/>
</button>
</Button>
</ShadTooltip>
</div>
</div>

View file

@ -2,16 +2,15 @@ import ComponentsComponent from "../componentsComponent";
import HeaderTabsSearchComponent from "./components/headerTabsSearchComponent";
type MyCollectionComponentProps = {
key: string;
type: string;
};
const MyCollectionComponent = ({ key, type }: MyCollectionComponentProps) => {
const MyCollectionComponent = ({ type }: MyCollectionComponentProps) => {
return (
<>
<HeaderTabsSearchComponent />
<div className="mt-5 flex h-full flex-col">
<ComponentsComponent key={key} type={type} />
<ComponentsComponent key={type} type={type} />
</div>
</>
);

View file

@ -16,7 +16,7 @@ import useDropdownOptions from "../../hooks/use-dropdown-options";
export default function HomePage(): JSX.Element {
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId
(state) => state.setCurrentFlowId,
);
const location = useLocation();
@ -67,10 +67,6 @@ export default function HomePage(): JSX.Element {
<div className="flex h-full w-full space-y-8 md:flex-col lg:flex-row lg:space-x-8 lg:space-y-0">
<aside className="flex h-fit w-fit flex-col space-y-6">
<SidebarNav
handleOpenNewFolderModal={() => {
setFolderToEdit(null);
setOpenFolderModal(true);
}}
items={[]}
handleChangeFolder={(id: string) => {
navigate(`all/folder/${id}`, { state: { folderId: id } });

View file

@ -8,7 +8,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
export default function SettingsPage(): JSX.Element {
const pathname = location.pathname;
const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId
(state) => state.setCurrentFlowId,
);
useEffect(() => {
setCurrentFlowId("");
@ -59,7 +59,10 @@ export default function SettingsPage(): JSX.Element {
title: "Messages",
href: "/settings/messages",
icon: (
<ForwardedIconComponent name="Keyboard" className="w-5 stroke-[1.5]" />
<ForwardedIconComponent
name="MessagesSquare"
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
/>
),
},
];
@ -72,8 +75,10 @@ export default function SettingsPage(): JSX.Element {
<aside className="flex h-full shrink-0 flex-col space-y-6 lg:w-[20vw]">
<SidebarNav items={sidebarNavItems} />
</aside>
<div className="h-full w-full flex-1 pb-8">
<Outlet />
<div className="flex h-full w-full flex-1 flex-col">
<div className="flex-1 pb-8">
<Outlet />
</div>
</div>
</div>
</PageLayout>

View file

@ -52,23 +52,17 @@ export default function ApiKeysPage() {
/>
<div className="flex h-full w-full flex-col justify-between">
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
<CardContent className="h-full">
<TableComponent
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(
event.api.getSelectedRows().map((row) => row.id),
);
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={columnDefs}
rowData={keysList.current}
/>
</CardContent>
</Card>
<TableComponent
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(event.api.getSelectedRows().map((row) => row.id));
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={columnDefs}
rowData={keysList.current}
/>
</div>
</div>
);

View file

@ -16,13 +16,13 @@ import { cn } from "../../../../utils/utils";
export default function GlobalVariablesPage() {
const globalVariablesEntries = useGlobalVariablesStore(
(state) => state.globalVariablesEntries
(state) => state.globalVariablesEntries,
);
const removeGlobalVariable = useGlobalVariablesStore(
(state) => state.removeGlobalVariable
(state) => state.removeGlobalVariable,
);
const globalVariables = useGlobalVariablesStore(
(state) => state.globalVariables
(state) => state.globalVariables,
);
const setErrorData = useAlertStore((state) => state.setErrorData);
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
@ -154,7 +154,7 @@ export default function GlobalVariablesPage() {
<IconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-destructive group-disabled:text-primary"
"h-5 w-5 text-destructive group-disabled:text-primary",
)}
/>
</Button>
@ -168,23 +168,17 @@ export default function GlobalVariablesPage() {
</div>
<div className="flex h-full w-full flex-col justify-between">
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
<CardContent className="h-full">
<TableComponent
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(
event.api.getSelectedRows().map((row) => row.name)
);
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={colDefs}
rowData={rowData}
/>
</CardContent>
</Card>
<TableComponent
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(event.api.getSelectedRows().map((row) => row.name));
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={colDefs}
rowData={rowData}
/>
</div>
</div>
);

View file

@ -20,7 +20,7 @@ export default function ShortcutsPage() {
const redoShortcut = `${isMac ? "Cmd" : "Ctrl"} + Y`;
// Column Definitions: Defines the columns to be displayed.
const [colDefs, setColDefs] = useState<(ColDef<any> | ColGroupDef<any>)[]>([
const colDefs = [
{
headerName: "Functionality",
field: "name",
@ -33,9 +33,9 @@ export default function ShortcutsPage() {
editable: false,
resizable: false,
},
]);
];
const [nodesRowData, setNodesRowData] = useState([
const nodesRowData = [
{
name: "Advanced Settings Component",
shortcut: advancedShortcut,
@ -96,7 +96,7 @@ export default function ShortcutsPage() {
shortcut: redoShortcut,
resizable: false,
},
]);
];
return (
<div className="flex h-full w-full flex-col gap-6">
@ -114,17 +114,12 @@ export default function ShortcutsPage() {
</p>
</div>
</div>
<div className="grid gap-6">
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
<CardContent className="h-full">
<TableComponent
domLayout="autoHeight"
pagination={false}
columnDefs={colDefs}
rowData={nodesRowData}
/>
</CardContent>
</Card>
<div className="flex h-full w-full flex-col justify-between">
<TableComponent
pagination={false}
columnDefs={colDefs}
rowData={nodesRowData}
/>
</div>
</div>
);

View file

@ -27,7 +27,7 @@ export default function MessagesPage() {
setSelectedRows,
setSuccessData,
setErrorData,
selectedRows
selectedRows,
);
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
@ -52,29 +52,25 @@ export default function MessagesPage() {
handleRemoveMessages={handleRemoveMessages}
/>
<div className="flex h-full w-full flex-col justify-between pb-8">
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
<CardContent className="h-full">
<TableComponent
readOnlyEdit
onCellEditRequest={(event) => {
handleUpdateMessage(event);
}}
editable={["Sender Name", "Message"]}
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(
event.api.getSelectedRows().map((row) => row.index)
);
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={columns}
rowData={messages}
/>
</CardContent>
</Card>
<div className="flex h-full w-full flex-col justify-between">
<TableComponent
readOnlyEdit
onCellEditRequest={(event) => {
handleUpdateMessage(event);
}}
editable={["Sender Name", "Message"]}
overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(
event.api.getSelectedRows().map((row) => row.index),
);
}}
rowSelection="multiple"
suppressRowClickSelection={true}
pagination={true}
columnDefs={columns}
rowData={messages}
/>
</div>
</div>
);

View file

@ -1,5 +1,5 @@
import * as debounce from "debounce-promise";
import { cloneDeep } from "lodash";
import pDebounce from "p-debounce";
import { Edge, Node, Viewport, XYPosition } from "reactflow";
import { create } from "zustand";
import { SAVE_DEBOUNCE_TIME } from "../constants/constants";
@ -128,7 +128,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
set({ saveLoading: true }); // set saveLoading true immediately
return get().saveFlowDebounce(flow, silent); // call the debounced function directly
},
saveFlowDebounce: debounce((flow: FlowType, silent?: boolean) => {
saveFlowDebounce: pDebounce((flow: FlowType, silent?: boolean) => {
set({ saveLoading: true });
return new Promise<void>((resolve, reject) => {
updateFlowInDatabase(flow)

View file

@ -7,6 +7,7 @@ import {
} from "../pages/MainPage/services";
import { FoldersStoreType } from "../types/zustand/folders";
import useFlowsManagerStore from "./flowsManagerStore";
import { uploadFlowsToDatabase } from "../controllers/API";
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
folders: [],
@ -17,18 +18,18 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
getFolders().then(
(res) => {
const foldersWithoutStarterProjects = res.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME
(folder) => folder.name !== STARTER_FOLDER_NAME,
);
const starterProjects = res.find(
(folder) => folder.name === STARTER_FOLDER_NAME
(folder) => folder.name === STARTER_FOLDER_NAME,
);
set({ starterProjectId: starterProjects!.id ?? "" });
set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find(
(f) => f.name === DEFAULT_FOLDER
(f) => f.name === DEFAULT_FOLDER,
)?.id;
set({ myCollectionId });
@ -41,11 +42,11 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
get().setLoading(false);
resolve();
},
() => {
(error) => {
set({ folders: [] });
get().setLoading(false);
reject();
}
reject(error);
},
);
}
});
@ -65,7 +66,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
},
() => {
get().setLoadingById(false);
}
},
);
}
},
@ -100,7 +101,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
folderIdDragging: "",
setFolderIdDragging: (id) => set(() => ({ folderIdDragging: id })),
uploadFolder: () => {
return new Promise<void>(() => {
return new Promise<void>((resolve, reject) => {
const input = document.createElement("input");
input.type = "file";
input.onchange = (event: Event) => {
@ -111,8 +112,31 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
const file = (event.target as HTMLInputElement).files![0];
const formData = new FormData();
formData.append("file", file);
uploadFlowsFromFolders(formData).then(() => {
get().getFoldersApi(true);
file.text().then((text) => {
const data = JSON.parse(text);
if (data.data?.nodes) {
useFlowsManagerStore
.getState()
.addFlow(true, data)
.then(() => {
resolve();
})
.catch((error) => {
reject(error);
});
} else {
uploadFlowsFromFolders(formData)
.then(() => {
get()
.getFoldersApi(true)
.then(() => {
resolve();
});
})
.catch((error) => {
reject(error);
});
}
});
}
};

View file

@ -20,7 +20,7 @@ export type FoldersStoreType = {
setFolderUrl: (folderUrl: string) => void;
folderDragging: boolean;
setFolderDragging: (set: boolean) => void;
uploadFolder: (folderId: string) => void;
uploadFolder: (folderId: string) => Promise<void>;
folderIdDragging: string;
setFolderIdDragging: (id: string) => void;
starterProjectId: string;

View file

@ -8,6 +8,7 @@ import {
XYPosition,
} from "reactflow";
import ShortUniqueId from "short-unique-id";
import getFieldTitle from "../CustomNodes/utils/get-field-title";
import {
INPUT_TYPES,
LANGFLOW_SUPPORTED_TYPES,
@ -16,7 +17,6 @@ import {
specialCharsRegex,
} from "../constants/constants";
import { downloadFlowsFromDatabase } from "../controllers/API";
import getFieldTitle from "../customNodes/utils/get-field-title";
import { DESCRIPTIONS } from "../flow_constants";
import {
APIClassType,

View file

@ -56,6 +56,7 @@ import {
FolderIcon,
FolderPlus,
FolderPlusIcon,
FolderUp,
FormInput,
Forward,
Gift,
@ -431,6 +432,7 @@ export const nodeIconsLucide: iconsType = {
ChevronLeft,
SlidersHorizontal,
Palette,
FolderUp,
Blocks,
ChevronDown,
ArrowLeft,