Merge branch 'dev' into docs-explain-io-playground
This commit is contained in:
commit
25307e7509
62 changed files with 1276 additions and 962 deletions
3
.github/workflows/docker-build.yml
vendored
3
.github/workflows/docker-build.yml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
4
.github/workflows/pre-release.yml
vendored
4
.github/workflows/pre-release.yml
vendored
|
|
@ -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: |
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
||||
|
|
|
|||
|
|
@ -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**.
|
||||
|
||||
|
|
|
|||
113
docs/docs/deployment/backend-only.md
Normal file
113
docs/docs/deployment/backend-only.md
Normal 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.
|
||||
65
docs/docs/deployment/docker.md
Normal file
65
docs/docs/deployment/docker.md
Normal 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`.
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
4
docs/static/logos/twitter.svg
vendored
4
docs/static/logos/twitter.svg
vendored
|
|
@ -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 |
1100
poetry.lock
generated
1100
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
from .AstraDBSearch import AstraDBSearchComponent
|
||||
from .ChromaSearch import ChromaSearchComponent
|
||||
from .FAISSSearch import FAISSSearchComponent
|
||||
from .MongoDBAtlasVectorSearch import MongoDBAtlasSearchComponent
|
||||
from .PineconeSearch import PineconeSearchComponent
|
||||
from .QdrantSearch import QdrantSearchComponent
|
||||
from .RedisSearch import RedisSearchComponent
|
||||
from .SupabaseVectorStoreSearch import SupabaseSearchComponent
|
||||
from .VectaraSearch import VectaraSearchComponent
|
||||
from .WeaviateSearch import WeaviateSearchVectorStore
|
||||
from .pgvectorSearch import PGVectorSearchComponent
|
||||
from .Couchbase import CouchbaseSearchComponent # type: ignore
|
||||
|
||||
__all__ = [
|
||||
"AstraDBSearchComponent",
|
||||
"ChromaSearchComponent",
|
||||
"CouchbaseSearchComponent",
|
||||
"FAISSSearchComponent",
|
||||
"MongoDBAtlasSearchComponent",
|
||||
"PineconeSearchComponent",
|
||||
"QdrantSearchComponent",
|
||||
"RedisSearchComponent",
|
||||
"SupabaseSearchComponent",
|
||||
"VectaraSearchComponent",
|
||||
"WeaviateSearchVectorStore",
|
||||
"PGVectorSearchComponent",
|
||||
]
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from .AstraDB import AstraDBVectorStoreComponent
|
||||
from .Chroma import ChromaComponent
|
||||
from .FAISS import FAISSComponent
|
||||
from .MongoDBAtlasVector import MongoDBAtlasComponent
|
||||
from .Pinecone import PineconeComponent
|
||||
from .Qdrant import QdrantComponent
|
||||
from .Redis import RedisComponent
|
||||
from .SupabaseVectorStore import SupabaseComponent
|
||||
from .Vectara import VectaraComponent
|
||||
from .Weaviate import WeaviateVectorStoreComponent
|
||||
from .pgvector import PGVectorComponent
|
||||
from .Couchbase import CouchbaseComponent
|
||||
|
||||
__all__ = [
|
||||
"AstraDBVectorStoreComponent",
|
||||
"ChromaComponent",
|
||||
"CouchbaseComponent",
|
||||
"FAISSComponent",
|
||||
"MongoDBAtlasComponent",
|
||||
"PineconeComponent",
|
||||
"QdrantComponent",
|
||||
"RedisComponent",
|
||||
"SupabaseComponent",
|
||||
"VectaraComponent",
|
||||
"WeaviateVectorStoreComponent",
|
||||
"base",
|
||||
"PGVectorComponent",
|
||||
]
|
||||
|
|
@ -297,7 +297,7 @@ class CodeParser:
|
|||
bases = self.execute_and_inspect_classes(self.code)
|
||||
except Exception as e:
|
||||
# If the code cannot be executed, return an empty list
|
||||
logger.exception(e)
|
||||
logger.debug(e)
|
||||
bases = []
|
||||
raise e
|
||||
return bases
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
51
src/backend/base/poetry.lock
generated
51
src/backend/base/poetry.lock
generated
|
|
@ -1159,13 +1159,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "langchain"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain-0.2.2-py3-none-any.whl", hash = "sha256:58ca0c47bcdd156da66f50a0a4fcedc49bf6950827f4a6b06c8c4842d55805f3"},
|
||||
{file = "langchain-0.2.2.tar.gz", hash = "sha256:9d61e50e9cdc2bea659bc5e6c03650ba048fda63a307490ae368e539f61a0d3a"},
|
||||
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"},
|
||||
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1181,29 +1181,15 @@ requests = ">=2,<3"
|
|||
SQLAlchemy = ">=1.4,<3"
|
||||
tenacity = ">=8.1.0,<9.0.0"
|
||||
|
||||
[package.extras]
|
||||
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
|
||||
clarifai = ["clarifai (>=9.1.0)"]
|
||||
cli = ["typer (>=0.9.0,<0.10.0)"]
|
||||
cohere = ["cohere (>=4,<6)"]
|
||||
docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"]
|
||||
embeddings = ["sentence-transformers (>=2,<3)"]
|
||||
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
|
||||
javascript = ["esprima (>=4.0.1,<5.0.0)"]
|
||||
llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"]
|
||||
openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"]
|
||||
qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"]
|
||||
text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-community"
|
||||
version = "0.2.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]
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
17
src/frontend/package-lock.json
generated
17
src/frontend/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) => ({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 } });
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue