Merge remote-tracking branch 'origin/two_edges_dev' into two_edges

This commit is contained in:
ogabrielluiz 2024-06-14 14:09:12 -03:00
commit 6f40941925
83 changed files with 1427 additions and 2465 deletions

View file

@ -23,14 +23,23 @@ env:
POETRY_VERSION: "1.8.2" POETRY_VERSION: "1.8.2"
jobs: jobs:
docker_build: setup:
name: Build Docker Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
base_tags: ${{ steps.set-vars.outputs.base_tags }}
main_tags: ${{ steps.set-vars.outputs.main_tags }}
steps:
- uses: actions/checkout@v4
- name: Set Dockerfile and Tags
id: set-vars
run: |
echo "base_tags=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_OUTPUT
echo "main_tags=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_OUTPUT
build_base:
runs-on: ubuntu-latest
needs: setup
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
id: qemu
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub - name: Login to Docker Hub
@ -38,56 +47,54 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set Dockerfile and Tags - name: Build and push Base Image
id: set-vars
run: |
if [ "${{ inputs.release_type }}" == "base" ]; then
echo "DOCKERFILE=./docker/build_and_push_base.Dockerfile" >> $GITHUB_ENV
echo "TAGS=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_ENV
else
echo "DOCKERFILE=./docker/build_and_push.Dockerfile" >> $GITHUB_ENV
echo "TAGS=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_ENV
fi
- name: Build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
platforms: "linux/amd64,linux/arm64/v8" platforms: "linux/amd64,linux/arm64/v8"
file: ${{ env.DOCKERFILE }} file: ./docker/build_and_push_base.Dockerfile
tags: ${{ env.TAGS }} tags: ${{ needs.setup.outputs.base_tags }}
- name: Wait for Docker Hub to propagate
run: sleep 120 build_components:
- name: Build and push (backend)
if: ${{ inputs.release_type == 'main' }} if: ${{ inputs.release_type == 'main' }}
runs-on: ubuntu-latest
needs: build_base
strategy:
matrix:
component: [backend, frontend]
include:
- component: backend
dockerfile: ./docker/build_and_push_backend.Dockerfile
tags: langflowai/langflow-backend:${{ inputs.version }},langflowai/langflow-backend:1.0-alpha
- component: frontend
dockerfile: ./docker/frontend/build_and_push_frontend.Dockerfile
tags: langflowai/langflow-frontend:${{ inputs.version }},langflowai/langflow-frontend:1.0-alpha
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push ${{ matrix.component }}
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
platforms: "linux/amd64,linux/arm64/v8" platforms: "linux/amd64,linux/arm64/v8"
file: ./docker/build_and_push_backend.Dockerfile
build-args: | build-args: |
LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }}
tags: | file: ${{ matrix.dockerfile }}
langflowai/langflow-backend:${{ inputs.version }} tags: ${{ matrix.tags }}
langflowai/langflow-backend:1.0-alpha
- name: Build and push (frontend)
if: ${{ inputs.release_type == 'main' }}
uses: docker/build-push-action@v5
with:
context: .
push: true
file: ./docker/frontend/build_and_push_frontend.Dockerfile
platforms: "linux/amd64,linux/arm64/v8"
tags: |
langflowai/langflow-frontend:${{ inputs.version }}
langflowai/langflow-frontend:1.0-alpha
restart-space: restart-space:
name: Restart HuggingFace Spaces name: Restart HuggingFace Spaces
if: ${{ inputs.release_type == 'main' }} if: ${{ inputs.release_type == 'main' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: docker_build needs: build_base
strategy: strategy:
matrix: matrix:
python-version: python-version:

View file

@ -24,6 +24,7 @@ env:
jobs: jobs:
test-docker: test-docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Test docker images
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build image - name: Build image
@ -61,20 +62,3 @@ jobs:
docker build -t langflowai/langflow-frontend:latest-dev \ docker build -t langflowai/langflow-frontend:latest-dev \
-f docker/frontend/build_and_push_frontend.Dockerfile \ -f docker/frontend/build_and_push_frontend.Dockerfile \
. .
test-multi-arch-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
id: qemu
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: false
file: ./docker/build_and_push.Dockerfile
platforms: "linux/amd64,linux/arm64/v8"
tags: langflowai/langflow:latest-dev

51
.github/workflows/lint-js.yml vendored Normal file
View file

@ -0,0 +1,51 @@
name: Lint Frontend
on:
pull_request:
paths:
- "src/frontend/**"
env:
NODE_VERSION: "21"
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
id: setup-node
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache Node.js dependencies
uses: actions/cache@v4
id: npm-cache
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('src/frontend/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install Node.js dependencies
run: |
cd src/frontend
npm ci
if: ${{ steps.setup-node.outputs.cache-hit != 'true' }}
- name: Run linters
uses: wearerequired/lint-action@v1
with:
github_token: ${{ secrets.github_token }}
auto_fix: true
git_email: "gabriel@langflow.org"
# Enable linters
eslint: true
prettier: true
prettier_args: '--write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore'

View file

@ -1,17 +1,12 @@
name: lint name: Lint Python
on: on:
push:
branches: [main]
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
pull_request: pull_request:
paths: paths:
- "poetry.lock" - "poetry.lock"
- "pyproject.toml" - "pyproject.toml"
- "src/backend/**" - "src/backend/**"
- "tests/**"
env: env:
POETRY_VERSION: "1.8.2" POETRY_VERSION: "1.8.2"
@ -45,6 +40,13 @@ jobs:
path: | path: |
./.mypy_cache ./.mypy_cache
key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }} key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }}
- name: Lint check - name: Run linters
run: | uses: wearerequired/lint-action@v1
make lint with:
github_token: ${{ secrets.github_token }}
# Enable linters
git_email: "gabriel@langflow.org"
mypy: true
mypy_args: '--namespace-packages -p "langflow"'
mypy_command_prefix: "poetry run"

14
.github/workflows/matchers/ruff.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"problemMatcher": [
{
"owner": "ruff",
"pattern": [
{
"regexp": "^(Would reformat): (.+)$",
"message": 1,
"file": 2
}
]
}
]
}

39
.github/workflows/style-check-py.yml vendored Normal file
View file

@ -0,0 +1,39 @@
name: Ruff Style Check
on:
pull_request:
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
- "tests/**"
env:
POETRY_VERSION: "1.8.2"
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.12"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_caching"
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
cache-key: ${{ runner.os }}-poetry-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }}
- name: Install Python dependencies
run: |
poetry env use ${{ matrix.python-version }}
poetry install
- name: Register problem matcher
run: echo "::add-matcher::.github/workflows/matchers/ruff.json"
- name: Run Ruff
run: poetry run ruff check --output-format=github .
- name: Run Ruff format
run: poetry run ruff format --check .

2
.gitignore vendored
View file

@ -180,6 +180,8 @@ coverage.xml
local_settings.py local_settings.py
db.sqlite3 db.sqlite3
db.sqlite3-journal db.sqlite3-journal
*.db-shm
*.db-wal
# Flask stuff: # Flask stuff:
instance/ instance/

View file

@ -1,18 +1,5 @@
fail_fast: true fail_fast: true
repos: repos:
- repo: https://github.com/pre-commit/mirrors-eslint
rev: "v9.1.1"
hooks:
- id: eslint
files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx
types: [file]
args: ["--fix", "--no-warn-ignored"]
additional_dependencies:
- eslint@9.1.1
- eslint-plugin-prettier
- eslint-config-prettier
- prettier
- eslint-plugin-react@latest
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0 rev: v4.1.0
hooks: hooks:
@ -25,16 +12,3 @@ repos:
args: args:
- --fix=lf - --fix=lf
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.2
hooks:
# Run the linter.
- id: ruff
# Python
files: \.py$
types: [file]
# Run the formatter.
- id: ruff-format
files: \.py$
types: [file]

View file

@ -9,17 +9,21 @@ open_browser ?= true
path = src/backend/base/langflow/frontend path = src/backend/base/langflow/frontend
workers ?= 1 workers ?= 1
codespell: codespell:
@poetry install --with spelling @poetry install --with spelling
poetry run codespell --toml pyproject.toml poetry run codespell --toml pyproject.toml
fix_codespell: fix_codespell:
@poetry install --with spelling @poetry install --with spelling
poetry run codespell --toml pyproject.toml --write poetry run codespell --toml pyproject.toml --write
setup_poetry: setup_poetry:
pipx install poetry pipx install poetry
add: add:
@echo 'Adding dependencies' @echo 'Adding dependencies'
ifdef devel ifdef devel
@ -34,45 +38,54 @@ ifdef base
cd src/backend/base && poetry add $(base) cd src/backend/base && poetry add $(base)
endif endif
init: init:
@echo 'Installing backend dependencies' @echo 'Installing backend dependencies'
make install_backend make install_backend
@echo 'Installing frontend dependencies' @echo 'Installing frontend dependencies'
make install_frontend make install_frontend
coverage:
coverage: ## run the tests and generate a coverage report
poetry run pytest --cov \ poetry run pytest --cov \
--cov-config=.coveragerc \ --cov-config=.coveragerc \
--cov-report xml \ --cov-report xml \
--cov-report term-missing:skip-covered \ --cov-report term-missing:skip-covered \
--cov-report lcov:coverage/lcov-pytest.info --cov-report lcov:coverage/lcov-pytest.info
# allow passing arguments to pytest # allow passing arguments to pytest
tests: tests: ## run the tests
poetry run pytest tests --instafail -ra -n auto -m "not api_key_required" $(args) poetry run pytest tests --instafail -ra -n auto -m "not api_key_required" $(args)
format: format: ## run code formatters
poetry run ruff check . --fix poetry run ruff check . --fix
poetry run ruff format . poetry run ruff format .
cd src/frontend && npm run format cd src/frontend && npm run format
lint:
lint: ## run linters
poetry run mypy --namespace-packages -p "langflow" poetry run mypy --namespace-packages -p "langflow"
install_frontend:
install_frontend: ## install the frontend dependencies
cd src/frontend && npm install cd src/frontend && npm install
install_frontendci: install_frontendci:
cd src/frontend && npm ci cd src/frontend && npm ci
install_frontendc: install_frontendc:
cd src/frontend && rm -rf node_modules package-lock.json && npm install cd src/frontend && rm -rf node_modules package-lock.json && npm install
run_frontend: run_frontend:
@-kill -9 `lsof -t -i:3000` @-kill -9 `lsof -t -i:3000`
cd src/frontend && npm start cd src/frontend && npm start
tests_frontend: tests_frontend:
ifeq ($(UI), true) ifeq ($(UI), true)
cd src/frontend && npx playwright test --ui --project=chromium cd src/frontend && npx playwright test --ui --project=chromium
@ -80,6 +93,7 @@ else
cd src/frontend && npx playwright test --project=chromium cd src/frontend && npx playwright test --project=chromium
endif endif
run_cli: run_cli:
@echo 'Running the CLI' @echo 'Running the CLI'
@make install_frontend > /dev/null @make install_frontend > /dev/null
@ -93,6 +107,7 @@ else
@make start host=$(host) port=$(port) log_level=$(log_level) @make start host=$(host) port=$(port) log_level=$(log_level)
endif endif
run_cli_debug: run_cli_debug:
@echo 'Running the CLI in debug mode' @echo 'Running the CLI in debug mode'
@make install_frontend > /dev/null @make install_frontend > /dev/null
@ -106,6 +121,7 @@ else
@make start host=$(host) port=$(port) log_level=debug @make start host=$(host) port=$(port) log_level=debug
endif endif
start: start:
@echo 'Running the CLI' @echo 'Running the CLI'
@ -116,30 +132,34 @@ else
endif endif
setup_devcontainer: setup_devcontainer:
make init make init
make build_frontend make build_frontend
poetry run langflow --path src/frontend/build poetry run langflow --path src/frontend/build
setup_env: setup_env:
@sh ./scripts/setup/update_poetry.sh 1.8.2 @sh ./scripts/setup/update_poetry.sh 1.8.2
@sh ./scripts/setup/setup_env.sh @sh ./scripts/setup/setup_env.sh
frontend:
frontend: ## run the frontend in development mode
make install_frontend make install_frontend
make run_frontend make run_frontend
frontendc: frontendc:
make install_frontendc make install_frontendc
make run_frontend make run_frontend
install_backend: install_backend:
@echo 'Installing backend dependencies' @echo 'Installing backend dependencies'
@poetry install @poetry install
@poetry run pre-commit install @poetry run pre-commit install
backend:
backend: ## run the backend in development mode
@echo 'Setting up the environment' @echo 'Setting up the environment'
@make setup_env @make setup_env
make install_backend make install_backend
@ -152,6 +172,7 @@ else
poetry run uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --env-file .env --loop asyncio --workers $(workers) poetry run uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --env-file .env --loop asyncio --workers $(workers)
endif endif
build_and_run: build_and_run:
@echo 'Removing dist folder' @echo 'Removing dist folder'
@make setup_env @make setup_env
@ -161,18 +182,21 @@ build_and_run:
poetry run pip install dist/*.tar.gz poetry run pip install dist/*.tar.gz
poetry run langflow run poetry run langflow run
build_and_install: build_and_install:
@echo 'Removing dist folder' @echo 'Removing dist folder'
rm -rf dist rm -rf dist
rm -rf src/backend/base/dist rm -rf src/backend/base/dist
make build && poetry run pip install dist/*.whl && pip install src/backend/base/dist/*.whl --force-reinstall make build && poetry run pip install dist/*.whl && pip install src/backend/base/dist/*.whl --force-reinstall
build_frontend:
build_frontend: ## build the frontend static files
cd src/frontend && CI='' npm run build cd src/frontend && CI='' npm run build
rm -rf src/backend/base/langflow/frontend rm -rf src/backend/base/langflow/frontend
cp -r src/frontend/build src/backend/base/langflow/frontend cp -r src/frontend/build src/backend/base/langflow/frontend
build:
build: ## build the frontend static files and package the project
@echo 'Building the project' @echo 'Building the project'
@make setup_env @make setup_env
ifdef base ifdef base
@ -185,13 +209,16 @@ ifdef main
make build_langflow make build_langflow
endif endif
build_langflow_base: build_langflow_base:
cd src/backend/base && poetry build cd src/backend/base && poetry build
rm -rf src/backend/base/langflow/frontend rm -rf src/backend/base/langflow/frontend
build_langflow_backup: build_langflow_backup:
poetry lock && poetry build poetry lock && poetry build
build_langflow: build_langflow:
cd ./scripts && poetry run python update_dependencies.py cd ./scripts && poetry run python update_dependencies.py
poetry lock poetry lock
@ -201,7 +228,8 @@ ifdef restore
mv poetry.lock.bak poetry.lock mv poetry.lock.bak poetry.lock
endif endif
dev:
dev: ## run the project in development mode with docker compose
make install_frontend make install_frontend
ifeq ($(build),1) ifeq ($(build),1)
@echo 'Running docker compose up with build' @echo 'Running docker compose up with build'
@ -211,25 +239,30 @@ else
docker compose $(if $(debug),-f docker-compose.debug.yml) up docker compose $(if $(debug),-f docker-compose.debug.yml) up
endif endif
lock_base: lock_base:
cd src/backend/base && poetry lock cd src/backend/base && poetry lock
lock_langflow: lock_langflow:
poetry lock poetry lock
lock: lock:
# Run both in parallel # Run both in parallel
@echo 'Locking dependencies' @echo 'Locking dependencies'
cd src/backend/base && poetry lock cd src/backend/base && poetry lock
poetry lock poetry lock
publish_base: publish_base:
cd src/backend/base && poetry publish cd src/backend/base && poetry publish
publish_langflow: publish_langflow:
poetry publish poetry publish
publish:
publish: ## build the frontend static files and package the project and publish it to PyPI
@echo 'Publishing the project' @echo 'Publishing the project'
ifdef base ifdef base
make publish_base make publish_base
@ -239,17 +272,11 @@ ifdef main
make publish_langflow make publish_langflow
endif endif
help:
help: ## show this help message
@echo '----' @echo '----'
@echo 'format - run code formatters' @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | \
@echo 'lint - run linters' sed -e 's/:.*##\s*/:/' \
@echo 'install_frontend - install the frontend dependencies' -e 's/^\(.\+\):\(.*\)/\\x1b[36mmake \1\\x1b[m:\2/' | \
@echo 'build_frontend - build the frontend static files' column -c2 -t -s :']]')"
@echo 'run_frontend - run the frontend in development mode'
@echo 'run_backend - run the backend in development mode'
@echo 'build - build the frontend static files and package the project'
@echo 'publish - build the frontend static files and package the project and publish it to PyPI'
@echo 'dev - run the project in development mode with docker compose'
@echo 'tests - run the tests'
@echo 'coverage - run the tests and generate a coverage report'
@echo '----' @echo '----'

View file

@ -7,8 +7,9 @@
# Used to build deps + create our virtual environment # Used to build deps + create our virtual environment
################################ ################################
# force platform to the current architecture to increase build speed time on multi-platform builds # 1. use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved
FROM --platform=$BUILDPLATFORM python:3.12-slim as builder-base # 2. do not add --platform=$BUILDPLATFORM because the pydantic binaries must be resolved for the final architecture
FROM python:3.12.3-slim as builder-base
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
\ \
@ -51,15 +52,27 @@ COPY pyproject.toml poetry.lock README.md ./
COPY src/ ./src COPY src/ ./src
COPY scripts/ ./scripts COPY scripts/ ./scripts
RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py
# 1. Install the dependencies using the current poetry.lock file to create reproducible builds
# 2. Do not install dev dependencies
# 3. Install all the extras to ensure all optionals are installed as well
# 4. --sync to ensure nothing else is in the environment
# 5. Build the wheel and install "langflow" package (mainly for version)
# Note: moving to build and installing the wheel will make the docker images not reproducible.
RUN $POETRY_HOME/bin/poetry lock --no-update \ RUN $POETRY_HOME/bin/poetry lock --no-update \
# install current lock file with fixed dependencies versions \
# do not install dev dependencies \
&& $POETRY_HOME/bin/poetry install --without dev --sync -E deploy -E couchbase -E cassio \
&& $POETRY_HOME/bin/poetry build -f wheel \ && $POETRY_HOME/bin/poetry build -f wheel \
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl --force-reinstall && $POETRY_HOME/bin/poetry run pip install dist/*.whl
################################ ################################
# RUNTIME # RUNTIME
# Setup user, utilities and copy the virtual environment only # Setup user, utilities and copy the virtual environment only
################################ ################################
FROM python:3.12-slim as runtime # 1. use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved
FROM python:3.12.3-slim as runtime
RUN apt-get -y update \ RUN apt-get -y update \
&& apt-get install --no-install-recommends -y \ && apt-get install --no-install-recommends -y \

View file

@ -10,7 +10,9 @@
# PYTHON-BASE # PYTHON-BASE
# Sets up all our shared environment variables # Sets up all our shared environment variables
################################ ################################
FROM python:3.12-slim as python-base
# use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved
FROM python:3.12.3-slim as python-base
# python # python
ENV PYTHONUNBUFFERED=1 \ ENV PYTHONUNBUFFERED=1 \

View file

@ -5,7 +5,7 @@
# BUILDER-BASE # BUILDER-BASE
################################ ################################
# force platform to the current architecture to increase build speed time on multi-platform builds # 1. force platform to the current architecture to increase build speed time on multi-platform builds
FROM --platform=$BUILDPLATFORM node:lts-bookworm-slim as builder-base FROM --platform=$BUILDPLATFORM node:lts-bookworm-slim as builder-base
COPY src/frontend /frontend COPY src/frontend /frontend

View file

@ -7,7 +7,7 @@ server {
gzip_vary on; gzip_vary on;
gzip_disable "MSIE [4-6] \."; gzip_disable "MSIE [4-6] \.";
listen 80; listen __FRONTEND_PORT__;
location / { location / {
root /usr/share/nginx/html; root /usr/share/nginx/html;

View file

@ -4,11 +4,20 @@ trap 'kill -TERM $PID' TERM INT
if [ -z "$BACKEND_URL" ]; then if [ -z "$BACKEND_URL" ]; then
BACKEND_URL="$1" BACKEND_URL="$1"
fi fi
if [ -z "$FRONTEND_PORT" ]; then
FRONTEND_PORT="$2"
fi
if [ -z "$FRONTEND_PORT" ]; then
FRONTEND_PORT="80"
fi
if [ -z "$BACKEND_URL" ]; then if [ -z "$BACKEND_URL" ]; then
echo "BACKEND_URL must be set as an environment variable or as first parameter. (e.g. http://localhost:7860)" echo "BACKEND_URL must be set as an environment variable or as first parameter. (e.g. http://localhost:7860)"
exit 1 exit 1
fi fi
echo "BACKEND_URL: $BACKEND_URL"
echo "FRONTEND_PORT: $FRONTEND_PORT"
sed -i "s|__BACKEND_URL__|$BACKEND_URL|g" /etc/nginx/conf.d/default.conf sed -i "s|__BACKEND_URL__|$BACKEND_URL|g" /etc/nginx/conf.d/default.conf
sed -i "s|__FRONTEND_PORT__|$FRONTEND_PORT|g" /etc/nginx/conf.d/default.conf
cat /etc/nginx/conf.d/default.conf cat /etc/nginx/conf.d/default.conf

51
poetry.lock generated
View file

@ -471,17 +471,17 @@ files = [
[[package]] [[package]]
name = "boto3" name = "boto3"
version = "1.34.125" version = "1.34.126"
description = "The AWS SDK for Python" description = "The AWS SDK for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "boto3-1.34.125-py3-none-any.whl", hash = "sha256:116d9eb3c26cf313a2e1e44ef704d1f98f9eb18e7628695d07b01b44a8683544"}, {file = "boto3-1.34.126-py3-none-any.whl", hash = "sha256:7f676daef674fe74f34ce4063228eccc6e60c811f574720e31f230296c4bf29a"},
{file = "boto3-1.34.125.tar.gz", hash = "sha256:31c4a5e4d6f9e6116be61ff654b424ddbd1afcdefe0e8b870c4796f9108eb1c6"}, {file = "boto3-1.34.126.tar.gz", hash = "sha256:7e8418b47dd43954a9088d504541bed8a42b6d06e712d02befba134c1c4d7c6d"},
] ]
[package.dependencies] [package.dependencies]
botocore = ">=1.34.125,<1.35.0" botocore = ">=1.34.126,<1.35.0"
jmespath = ">=0.7.1,<2.0.0" jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0" s3transfer = ">=0.10.0,<0.11.0"
@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]] [[package]]
name = "botocore" name = "botocore"
version = "1.34.125" version = "1.34.126"
description = "Low-level, data-driven core of boto 3." description = "Low-level, data-driven core of boto 3."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "botocore-1.34.125-py3-none-any.whl", hash = "sha256:71e97e7d2c088f1188ba6976441b5857a5425acd4aaa31b45d13119c9cb86424"}, {file = "botocore-1.34.126-py3-none-any.whl", hash = "sha256:7eff883c638fe30e0b036789df32d851e093d12544615a3b90062b42ac85bdbc"},
{file = "botocore-1.34.125.tar.gz", hash = "sha256:d2882be011ad5b16e7ab4a96360b5b66a0a7e175c1ea06dbf2de473c0a0a33d8"}, {file = "botocore-1.34.126.tar.gz", hash = "sha256:7a8ccb6a7c02456757a984a3a44331b6f51c94cb8b9b287cd045122fd177a4b0"},
] ]
[package.dependencies] [package.dependencies]
@ -4113,22 +4113,25 @@ adal = ["adal (>=1.0.2)"]
[[package]] [[package]]
name = "langchain" name = "langchain"
version = "0.2.3" version = "0.2.4"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"},
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"},
] ]
[package.dependencies] [package.dependencies]
aiohttp = ">=3.8.3,<4.0.0" aiohttp = ">=3.8.3,<4.0.0"
async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""}
langchain-core = ">=0.2.0,<0.3.0" langchain-core = ">=0.2.6,<0.3.0"
langchain-text-splitters = ">=0.2.0,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0"
langsmith = ">=0.1.17,<0.2.0" langsmith = ">=0.1.17,<0.2.0"
numpy = ">=1,<2" numpy = [
{version = ">=1,<2", markers = "python_version < \"3.12\""},
{version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""},
]
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
requests = ">=2,<3" requests = ">=2,<3"
@ -4224,19 +4227,19 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "0.2.5" version = "0.2.6"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"},
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"},
] ]
[package.dependencies] [package.dependencies]
jsonpatch = ">=1.33,<2.0" jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.1.66,<0.2.0" langsmith = ">=0.1.75,<0.2.0"
packaging = ">=23.2,<24.0" packaging = ">=23.2,<25"
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0" tenacity = ">=8.1.0,<9.0.0"
@ -4409,7 +4412,7 @@ six = "*"
[[package]] [[package]]
name = "langflow-base" name = "langflow-base"
version = "0.0.63" version = "0.0.68"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
optional = false optional = false
python-versions = ">=3.10,<3.13" python-versions = ">=3.10,<3.13"
@ -4506,13 +4509,13 @@ requests = ">=2,<3"
[[package]] [[package]]
name = "litellm" name = "litellm"
version = "1.40.9" version = "1.40.12"
description = "Library to easily interface with LLM API providers" description = "Library to easily interface with LLM API providers"
optional = false optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [ files = [
{file = "litellm-1.40.9-py3-none-any.whl", hash = "sha256:8a4107b9cce114d822de52cbc9bce56f8edc6620f19d0f2257e71834715fb366"}, {file = "litellm-1.40.12-py3-none-any.whl", hash = "sha256:42f1648507f29c60543ba5fdf35d38fc161694da043b201508225bae50d3328c"},
{file = "litellm-1.40.9.tar.gz", hash = "sha256:e0ea07d0b55001a6f60bba2b2ecd72d1f0dca07e656f63937adfdf45f31e5ad7"}, {file = "litellm-1.40.12.tar.gz", hash = "sha256:366bb9c3694b9ef59b3d073bb37ff9ca175ab4090dc187b0a11d2b21db3a6a5d"},
] ]
[package.dependencies] [package.dependencies]
@ -7850,13 +7853,13 @@ websockets = ">=11,<13"
[[package]] [[package]]
name = "redis" name = "redis"
version = "5.0.5" version = "5.0.6"
description = "Python client for Redis database and key-value store" description = "Python client for Redis database and key-value store"
optional = true optional = true
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "redis-5.0.5-py3-none-any.whl", hash = "sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada"}, {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"},
{file = "redis-5.0.5.tar.gz", hash = "sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae"}, {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"},
] ]
[package.dependencies] [package.dependencies]

View file

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

View file

@ -1,3 +1,4 @@
from typing import Optional
from langchain_core.embeddings import Embeddings from langchain_core.embeddings import Embeddings
from langchain_openai import AzureOpenAIEmbeddings from langchain_openai import AzureOpenAIEmbeddings
from pydantic.v1 import SecretStr from pydantic.v1 import SecretStr
@ -44,6 +45,11 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
"password": True, "password": True,
}, },
"code": {"show": False}, "code": {"show": False},
"dimensions": {
"display_name": "Dimensions",
"info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.",
"advanced": True,
},
} }
def build( def build(
@ -52,6 +58,7 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
azure_deployment: str, azure_deployment: str,
api_version: str, api_version: str,
api_key: str, api_key: str,
dimensions: Optional[int] = None,
) -> Embeddings: ) -> Embeddings:
if api_key: if api_key:
azure_api_key = SecretStr(api_key) azure_api_key = SecretStr(api_key)
@ -63,6 +70,7 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
azure_deployment=azure_deployment, azure_deployment=azure_deployment,
api_version=api_version, api_version=api_version,
api_key=azure_api_key, api_key=azure_api_key,
dimensions=dimensions,
) )
except Exception as e: except Exception as e:

View file

@ -84,6 +84,11 @@ class OpenAIEmbeddingsComponent(CustomComponent):
"advanced": True, "advanced": True,
}, },
"tiktoken_enable": {"display_name": "TikToken Enable", "advanced": True}, "tiktoken_enable": {"display_name": "TikToken Enable", "advanced": True},
"dimensions": {
"display_name": "Dimensions",
"info": "The number of dimensions the resulting output embeddings should have. Only supported by certain models.",
"advanced": True,
},
} }
def build( def build(
@ -109,6 +114,7 @@ class OpenAIEmbeddingsComponent(CustomComponent):
skip_empty: bool = False, skip_empty: bool = False,
tiktoken_enable: bool = True, tiktoken_enable: bool = True,
tiktoken_model_name: Optional[str] = None, tiktoken_model_name: Optional[str] = None,
dimensions: Optional[int] = None,
) -> Embeddings: ) -> Embeddings:
# This is to avoid errors with Vector Stores (e.g Chroma) # This is to avoid errors with Vector Stores (e.g Chroma)
if disallowed_special == ["all"]: if disallowed_special == ["all"]:
@ -140,4 +146,5 @@ class OpenAIEmbeddingsComponent(CustomComponent):
show_progress_bar=show_progress_bar, show_progress_bar=show_progress_bar,
skip_empty=skip_empty, skip_empty=skip_empty,
tiktoken_model_name=tiktoken_model_name, tiktoken_model_name=tiktoken_model_name,
dimensions=dimensions,
) )

View file

@ -53,7 +53,7 @@ class ChatOpenAIComponent(CustomComponent):
self, self,
max_tokens: Optional[int] = 0, max_tokens: Optional[int] = 0,
model_kwargs: NestedDict = {}, model_kwargs: NestedDict = {},
model_name: str = "gpt-4o", model_name: str = "gpt-3.5-turbo",
openai_api_base: Optional[str] = None, openai_api_base: Optional[str] = None,
openai_api_key: Optional[str] = None, openai_api_key: Optional[str] = None,
temperature: float = 0.7, temperature: float = 0.7,

View file

@ -28,7 +28,7 @@ class AstraDBSearchComponent(LCVectorStoreComponent):
"info": "The name of the collection within Astra DB where the vectors will be stored.", "info": "The name of the collection within Astra DB where the vectors will be stored.",
}, },
"token": { "token": {
"display_name": "Token", "display_name": "Astra DB Application Token",
"info": "Authentication token for accessing Astra DB.", "info": "Authentication token for accessing Astra DB.",
"password": True, "password": True,
}, },

View file

@ -3,7 +3,6 @@ from typing import List, Optional
import chromadb import chromadb
from chromadb.config import Settings from chromadb.config import Settings
from langchain_chroma import Chroma from langchain_chroma import Chroma
from langflow.components.vectorstores.base.model import LCVectorStoreComponent from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.field_typing import Embeddings, Text from langflow.field_typing import Embeddings, Text
from langflow.schema import Data from langflow.schema import Data
@ -104,10 +103,11 @@ class ChromaSearchComponent(LCVectorStoreComponent):
client = chromadb.HttpClient(settings=chroma_settings) client = chromadb.HttpClient(settings=chroma_settings)
if index_directory: if index_directory:
index_directory = self.resolve_path(index_directory) index_directory = self.resolve_path(index_directory)
vector_store = Chroma( vector_store = Chroma(
embedding_function=embedding, embedding_function=embedding,
collection_name=collection_name, collection_name=collection_name,
persist_directory=index_directory, persist_directory=index_directory or None,
client=client, client=client,
) )

View file

@ -25,7 +25,7 @@ class AstraDBVectorStoreComponent(CustomComponent):
"info": "The name of the collection within Astra DB where the vectors will be stored.", "info": "The name of the collection within Astra DB where the vectors will be stored.",
}, },
"token": { "token": {
"display_name": "Token", "display_name": "Astra DB Application Token",
"info": "Authentication token for accessing Astra DB.", "info": "Authentication token for accessing Astra DB.",
"password": True, "password": True,
}, },

View file

@ -1,9 +1,11 @@
from typing import Any, Union from enum import Enum
from typing import Any, Generator, Union
from langchain_core.documents import Document from langchain_core.documents import Document
from pydantic import BaseModel from pydantic import BaseModel
from langflow.interface.utils import extract_input_variables_from_prompt from langflow.interface.utils import extract_input_variables_from_prompt
from langflow.schema.message import Message
class UnbuiltObject: class UnbuiltObject:
@ -14,6 +16,16 @@ class UnbuiltResult:
pass pass
class ArtifactType(str, Enum):
TEXT = "text"
RECORD = "record"
OBJECT = "object"
ARRAY = "array"
STREAM = "stream"
UNKNOWN = "unknown"
MESSAGE = "message"
def validate_prompt(prompt: str): def validate_prompt(prompt: str):
"""Validate prompt.""" """Validate prompt."""
if extract_input_variables_from_prompt(prompt): if extract_input_variables_from_prompt(prompt):
@ -50,3 +62,37 @@ def serialize_field(value):
elif isinstance(value, str): elif isinstance(value, str):
return {"result": value} return {"result": value}
return value return value
def get_artifact_type(value, build_result) -> str:
result = ArtifactType.UNKNOWN
match value:
case Record():
result = ArtifactType.RECORD
case str():
result = ArtifactType.TEXT
case dict():
result = ArtifactType.OBJECT
case list():
result = ArtifactType.ARRAY
case Message():
result = ArtifactType.MESSAGE
if result == ArtifactType.UNKNOWN:
if isinstance(build_result, Generator):
result = ArtifactType.STREAM
elif isinstance(value, Message) and isinstance(value.text, Generator):
result = ArtifactType.STREAM
return result.value
def post_process_raw(raw, artifact_type: str):
if artifact_type == ArtifactType.STREAM.value:
raw = ""
return raw

View file

@ -181,6 +181,9 @@ def update_new_output(data):
} }
) )
deduplicated_outputs = [] deduplicated_outputs = []
if source_node is None:
source_node = {"data": {"node": {"outputs": []}}}
for output in source_node["data"]["node"]["outputs"]: for output in source_node["data"]["node"]["outputs"]:
if output["name"] not in [d["name"] for d in deduplicated_outputs]: if output["name"] not in [d["name"] for d in deduplicated_outputs]:
deduplicated_outputs.append(output) deduplicated_outputs.append(output)

View file

@ -8,17 +8,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-k39HS", "id": "OpenAIModel-k39HS",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-njtka", "id": "ChatOutput-njtka",
"inputTypes": [ "inputTypes": ["Text", "Message"],
"Text",
"Message"
],
"type": "str" "type": "str"
} }
}, },
@ -38,18 +33,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-uxBqP", "id": "Prompt-uxBqP",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": ["Prompt"]
"Prompt"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-k39HS", "id": "OpenAIModel-k39HS",
"inputTypes": [ "inputTypes": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"type": "str" "type": "str"
} }
}, },
@ -69,19 +58,12 @@
"dataType": "ChatInput", "dataType": "ChatInput",
"id": "ChatInput-P3fgL", "id": "ChatInput-P3fgL",
"name": "message", "name": "message",
"output_types": [ "output_types": ["Message"]
"Message"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "user_input", "fieldName": "user_input",
"id": "Prompt-uxBqP", "id": "Prompt-uxBqP",
"inputTypes": [ "inputTypes": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -102,16 +84,10 @@
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-uxBqP", "id": "Prompt-uxBqP",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "str", "Text"],
"object",
"str",
"Text"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"template": [ "template": ["user_input"]
"user_input"
]
}, },
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
@ -134,9 +110,7 @@
"method": "build_prompt", "method": "build_prompt",
"name": "prompt", "name": "prompt",
"selected": "Prompt", "selected": "Prompt",
"types": [ "types": ["Prompt"],
"Prompt"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -145,9 +119,7 @@
"method": "format_prompt", "method": "format_prompt",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -178,9 +150,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -201,12 +171,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -244,11 +209,7 @@
"display_name": "OpenAI", "display_name": "OpenAI",
"id": "OpenAIModel-k39HS", "id": "OpenAIModel-k39HS",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "Text", "str"],
"object",
"Text",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -286,9 +247,7 @@
"method": "text_response", "method": "text_response",
"name": "text_output", "name": "text_output",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -297,9 +256,7 @@
"method": "build_model", "method": "build_model",
"name": "model_output", "name": "model_output",
"selected": "BaseLanguageModel", "selected": "BaseLanguageModel",
"types": [ "types": ["BaseLanguageModel"],
"BaseLanguageModel"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -330,11 +287,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -354,9 +307,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -376,9 +327,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -398,9 +347,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -418,7 +365,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "str", "type": "str",
"value": "gpt-4o" "value": "gpt-3.5-turbo"
}, },
"openai_api_base": { "openai_api_base": {
"advanced": true, "advanced": true,
@ -427,9 +374,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.", "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -449,9 +394,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The OpenAI API Key to use for the OpenAI model.", "info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": true, "load_from_db": true,
"multiline": false, "multiline": false,
@ -471,9 +414,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Stream the response from the model. Streaming works only in Chat.", "info": "Stream the response from the model. Streaming works only in Chat.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -493,9 +434,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "System message to pass to the model.", "info": "System message to pass to the model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -515,9 +454,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -553,12 +490,7 @@
"data": { "data": {
"id": "ChatOutput-njtka", "id": "ChatOutput-njtka",
"node": { "node": {
"base_classes": [ "base_classes": ["Record", "Text", "str", "object"],
"Record",
"Text",
"str",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -583,9 +515,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -616,10 +546,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Message to be passed as output.", "info": "Message to be passed as output.",
"input_types": [ "input_types": ["Text", "Message"],
"Text",
"Message"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -639,17 +566,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -665,9 +587,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -687,9 +607,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -725,12 +643,7 @@
"data": { "data": {
"id": "ChatInput-P3fgL", "id": "ChatInput-P3fgL",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "Record", "str", "Text"],
"object",
"Record",
"str",
"Text"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -754,9 +667,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -807,17 +718,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -833,9 +739,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -855,9 +759,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,

View file

@ -13,12 +13,7 @@
"targetHandle": { "targetHandle": {
"fieldName": "reference_2", "fieldName": "reference_2",
"id": "Prompt-Rse03", "id": "Prompt-Rse03",
"inputTypes": [ "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
"Document",
"BaseOutputParser",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -39,17 +34,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-gi29P", "id": "OpenAIModel-gi29P",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-JPlxl", "id": "ChatOutput-JPlxl",
"inputTypes": [ "inputTypes": ["Text", "Message"],
"Text",
"Message"
],
"type": "str" "type": "str"
} }
}, },
@ -74,12 +64,7 @@
"targetHandle": { "targetHandle": {
"fieldName": "reference_1", "fieldName": "reference_1",
"id": "Prompt-Rse03", "id": "Prompt-Rse03",
"inputTypes": [ "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
"Document",
"BaseOutputParser",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -99,19 +84,12 @@
"dataType": "TextInput", "dataType": "TextInput",
"id": "TextInput-og8Or", "id": "TextInput-og8Or",
"name": "Text", "name": "Text",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "instructions", "fieldName": "instructions",
"id": "Prompt-Rse03", "id": "Prompt-Rse03",
"inputTypes": [ "inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
"Document",
"BaseOutputParser",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -131,18 +109,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-Rse03", "id": "Prompt-Rse03",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": ["Prompt"]
"Prompt"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-gi29P", "id": "OpenAIModel-gi29P",
"inputTypes": [ "inputTypes": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"type": "str" "type": "str"
} }
}, },
@ -164,18 +136,10 @@
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-Rse03", "id": "Prompt-Rse03",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "Text", "str"],
"object",
"Text",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"template": [ "template": ["reference_1", "reference_2", "instructions"]
"reference_1",
"reference_2",
"instructions"
]
}, },
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
@ -198,9 +162,7 @@
"method": "build_prompt", "method": "build_prompt",
"name": "prompt", "name": "prompt",
"selected": "Prompt", "selected": "Prompt",
"types": [ "types": ["Prompt"],
"Prompt"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -209,9 +171,7 @@
"method": "format_prompt", "method": "format_prompt",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -320,9 +280,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -358,9 +316,7 @@
"data": { "data": {
"id": "URL-HYPkR", "id": "URL-HYPkR",
"node": { "node": {
"base_classes": [ "base_classes": ["Record"],
"Record"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"urls": null "urls": null
@ -380,9 +336,7 @@
"method": "fetch_content", "method": "fetch_content",
"name": "data", "name": "data",
"selected": "Data", "selected": "Data",
"types": [ "types": ["Data"],
"Data"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -404,7 +358,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "code", "type": "code",
"value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def fetch_content(self) -> Data:\n urls = [url.strip() for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\nimport re\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"\n Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(http://|https://)?\" # http:// or https://\n r\"(([a-zA-Z0-9\\.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,}))\" # top-level domain\n r\"(:[0-9]{1,5})?\" # optional port\n r\"(\\/.*)?$\" # optional path\n )\n\n if not re.match(url_regex, string):\n raise ValueError(f\"Invalid URL: {string}\")\n\n return string\n\n def fetch_content(self) -> Data:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n"
}, },
"urls": { "urls": {
"advanced": false, "advanced": false,
@ -444,12 +398,7 @@
"data": { "data": {
"id": "ChatOutput-JPlxl", "id": "ChatOutput-JPlxl",
"node": { "node": {
"base_classes": [ "base_classes": ["Text", "Record", "object", "str"],
"Text",
"Record",
"object",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -474,9 +423,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -507,10 +454,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Message to be passed as output.", "info": "Message to be passed as output.",
"input_types": [ "input_types": ["Text", "Message"],
"Text",
"Message"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -530,17 +474,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -556,9 +495,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -578,9 +515,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -611,11 +546,7 @@
"data": { "data": {
"id": "OpenAIModel-gi29P", "id": "OpenAIModel-gi29P",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -653,9 +584,7 @@
"method": "text_response", "method": "text_response",
"name": "text_output", "name": "text_output",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -664,9 +593,7 @@
"method": "build_model", "method": "build_model",
"name": "model_output", "name": "model_output",
"selected": "BaseLanguageModel", "selected": "BaseLanguageModel",
"types": [ "types": ["BaseLanguageModel"],
"BaseLanguageModel"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -697,11 +624,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -721,9 +644,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -743,9 +664,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -765,9 +684,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -785,7 +702,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "str", "type": "str",
"value": "gpt-4o" "value": "gpt-3.5-turbo"
}, },
"openai_api_base": { "openai_api_base": {
"advanced": true, "advanced": true,
@ -794,9 +711,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.", "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -816,9 +731,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The OpenAI API Key to use for the OpenAI model.", "info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": true, "load_from_db": true,
"multiline": false, "multiline": false,
@ -838,9 +751,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Stream the response from the model. Streaming works only in Chat.", "info": "Stream the response from the model. Streaming works only in Chat.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -860,9 +771,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "System message to pass to the model.", "info": "System message to pass to the model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -882,9 +791,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -920,9 +827,7 @@
"data": { "data": {
"id": "URL-2cX90", "id": "URL-2cX90",
"node": { "node": {
"base_classes": [ "base_classes": ["Record"],
"Record"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"urls": null "urls": null
@ -942,9 +847,7 @@
"method": "fetch_content", "method": "fetch_content",
"name": "data", "name": "data",
"selected": "Data", "selected": "Data",
"types": [ "types": ["Data"],
"Data"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -966,7 +869,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "code", "type": "code",
"value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def fetch_content(self) -> Data:\n urls = [url.strip() for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n" "value": "from langchain_community.document_loaders.web_base import WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.inputs import StrInput\nfrom langflow.schema import Data\nfrom langflow.template import Output\n\nimport re\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n\n inputs = [\n StrInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, separated by commas.\",\n value=\"\",\n is_list=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"\n Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(http://|https://)?\" # http:// or https://\n r\"(([a-zA-Z0-9\\.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,}))\" # top-level domain\n r\"(:[0-9]{1,5})?\" # optional port\n r\"(\\/.*)?$\" # optional path\n )\n\n if not re.match(url_regex, string):\n raise ValueError(f\"Invalid URL: {string}\")\n\n return string\n\n def fetch_content(self) -> Data:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n loader = WebBaseLoader(web_paths=urls)\n docs = loader.load()\n data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n"
}, },
"urls": { "urls": {
"advanced": false, "advanced": false,
@ -1006,11 +909,7 @@
"data": { "data": {
"id": "TextInput-og8Or", "id": "TextInput-og8Or",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "Text", "str"],
"object",
"Text",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -1023,16 +922,12 @@
"field_order": [], "field_order": [],
"frozen": false, "frozen": false,
"icon": "type", "icon": "type",
"output_types": [ "output_types": ["Text"],
"Text"
],
"outputs": [ "outputs": [
{ {
"name": "Text", "name": "Text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"]
"Text"
]
} }
], ],
"template": { "template": {
@ -1062,10 +957,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Text or Record to be passed as input.", "info": "Text or Record to be passed as input.",
"input_types": [ "input_types": ["Record", "Text"],
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1085,9 +977,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,

View file

@ -7,19 +7,12 @@
"dataType": "File", "dataType": "File",
"id": "File-BzIs2", "id": "File-BzIs2",
"name": "data", "name": "data",
"output_types": [ "output_types": ["Data"]
"Data"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "Document", "fieldName": "Document",
"id": "Prompt-9DNZG", "id": "Prompt-9DNZG",
"inputTypes": [ "inputTypes": ["Document", "Message", "Data", "Text"],
"Document",
"Message",
"Data",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -35,19 +28,12 @@
"dataType": "ChatInput", "dataType": "ChatInput",
"id": "ChatInput-27Usy", "id": "ChatInput-27Usy",
"name": "message", "name": "message",
"output_types": [ "output_types": ["Message"]
"Message"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "Question", "fieldName": "Question",
"id": "Prompt-9DNZG", "id": "Prompt-9DNZG",
"inputTypes": [ "inputTypes": ["Document", "Message", "Data", "Text"],
"Document",
"Message",
"Data",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -63,18 +49,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-9DNZG", "id": "Prompt-9DNZG",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": ["Prompt"]
"Prompt"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-8b6nG", "id": "OpenAIModel-8b6nG",
"inputTypes": [ "inputTypes": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"type": "str" "type": "str"
} }
}, },
@ -90,17 +70,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-8b6nG", "id": "OpenAIModel-8b6nG",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-y4SCS", "id": "ChatOutput-y4SCS",
"inputTypes": [ "inputTypes": ["Text", "Message"],
"Text",
"Message"
],
"type": "str" "type": "str"
} }
}, },
@ -118,18 +93,11 @@
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-9DNZG", "id": "Prompt-9DNZG",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "str", "Text"],
"object",
"str",
"Text"
],
"beta": false, "beta": false,
"conditional_paths": [], "conditional_paths": [],
"custom_fields": { "custom_fields": {
"template": [ "template": ["Document", "Question"]
"Document",
"Question"
]
}, },
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
@ -151,9 +119,7 @@
"method": "build_prompt", "method": "build_prompt",
"name": "prompt", "name": "prompt",
"selected": "Prompt", "selected": "Prompt",
"types": [ "types": ["Prompt"],
"Prompt"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -162,9 +128,7 @@
"method": "format_prompt", "method": "format_prompt",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -178,12 +142,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Data", "Text"],
"Document",
"Message",
"Data",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -204,12 +163,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Data", "Text"],
"Document",
"Message",
"Data",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -248,9 +202,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -286,12 +238,7 @@
"data": { "data": {
"id": "ChatInput-27Usy", "id": "ChatInput-27Usy",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Record", "Text", "object"],
"str",
"Record",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -315,9 +262,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -368,17 +313,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -394,9 +334,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -416,9 +354,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -454,12 +390,7 @@
"data": { "data": {
"id": "ChatOutput-y4SCS", "id": "ChatOutput-y4SCS",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Record", "Text", "object"],
"str",
"Record",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -483,9 +414,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -516,10 +445,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Message to be passed as output.", "info": "Message to be passed as output.",
"input_types": [ "input_types": ["Text", "Message"],
"Text",
"Message"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -539,17 +465,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -565,9 +486,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -587,9 +506,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -627,9 +544,7 @@
"display_name": "File", "display_name": "File",
"id": "File-BzIs2", "id": "File-BzIs2",
"node": { "node": {
"base_classes": [ "base_classes": ["Data"],
"Data"
],
"beta": false, "beta": false,
"conditional_paths": [], "conditional_paths": [],
"custom_fields": {}, "custom_fields": {},
@ -637,10 +552,7 @@
"display_name": "File", "display_name": "File",
"documentation": "", "documentation": "",
"edited": true, "edited": true,
"field_order": [ "field_order": ["path", "silent_errors"],
"path",
"silent_errors"
],
"frozen": false, "frozen": false,
"icon": "file-text", "icon": "file-text",
"output_types": [], "output_types": [],
@ -651,9 +563,7 @@
"method": "load_file", "method": "load_file",
"name": "data", "name": "data",
"selected": "Data", "selected": "Data",
"types": [ "types": ["Data"],
"Data"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -750,10 +660,7 @@
"data": { "data": {
"id": "OpenAIModel-8b6nG", "id": "OpenAIModel-8b6nG",
"node": { "node": {
"base_classes": [ "base_classes": ["BaseLanguageModel", "Text"],
"BaseLanguageModel",
"Text"
],
"beta": false, "beta": false,
"conditional_paths": [], "conditional_paths": [],
"custom_fields": {}, "custom_fields": {},
@ -781,9 +688,7 @@
"method": "text_response", "method": "text_response",
"name": "text_output", "name": "text_output",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -792,9 +697,7 @@
"method": "build_model", "method": "build_model",
"name": "model_output", "name": "model_output",
"selected": "BaseLanguageModel", "selected": "BaseLanguageModel",
"types": [ "types": ["BaseLanguageModel"],
"BaseLanguageModel"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -824,11 +727,7 @@
"display_name": "Input", "display_name": "Input",
"dynamic": false, "dynamic": false,
"info": "", "info": "",
"input_types": [ "input_types": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"name": "input_value", "name": "input_value",
@ -883,7 +782,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "str", "type": "str",
"value": "gpt-4o" "value": "gpt-3.5-turbo"
}, },
"openai_api_base": { "openai_api_base": {
"advanced": true, "advanced": true,
@ -905,9 +804,7 @@
"display_name": "OpenAI API Key", "display_name": "OpenAI API Key",
"dynamic": false, "dynamic": false,
"info": "The OpenAI API Key to use for the OpenAI model.", "info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"load_from_db": true, "load_from_db": true,
"name": "openai_api_key", "name": "openai_api_key",
"password": true, "password": true,

View file

@ -8,19 +8,12 @@
"dataType": "MemoryComponent", "dataType": "MemoryComponent",
"id": "MemoryComponent-cdA1J", "id": "MemoryComponent-cdA1J",
"name": "text", "name": "text",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "context", "fieldName": "context",
"id": "Prompt-ODkUx", "id": "Prompt-ODkUx",
"inputTypes": [ "inputTypes": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -41,19 +34,12 @@
"dataType": "ChatInput", "dataType": "ChatInput",
"id": "ChatInput-t7F8v", "id": "ChatInput-t7F8v",
"name": "message", "name": "message",
"output_types": [ "output_types": ["Message"]
"Message"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "user_message", "fieldName": "user_message",
"id": "Prompt-ODkUx", "id": "Prompt-ODkUx",
"inputTypes": [ "inputTypes": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -74,18 +60,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-ODkUx", "id": "Prompt-ODkUx",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": ["Prompt"]
"Prompt"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-9RykF", "id": "OpenAIModel-9RykF",
"inputTypes": [ "inputTypes": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"type": "str" "type": "str"
} }
}, },
@ -105,17 +85,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-9RykF", "id": "OpenAIModel-9RykF",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-P1jEe", "id": "ChatOutput-P1jEe",
"inputTypes": [ "inputTypes": ["Text", "Message"],
"Text",
"Message"
],
"type": "str" "type": "str"
} }
}, },
@ -135,17 +110,12 @@
"dataType": "MemoryComponent", "dataType": "MemoryComponent",
"id": "MemoryComponent-cdA1J", "id": "MemoryComponent-cdA1J",
"name": "text", "name": "text",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "TextOutput-vrs6T", "id": "TextOutput-vrs6T",
"inputTypes": [ "inputTypes": ["Record", "Text"],
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -164,12 +134,7 @@
"data": { "data": {
"id": "ChatInput-t7F8v", "id": "ChatInput-t7F8v",
"node": { "node": {
"base_classes": [ "base_classes": ["Text", "object", "Record", "str"],
"Text",
"object",
"Record",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -193,9 +158,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -246,17 +209,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -272,9 +230,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -294,9 +250,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -332,12 +286,7 @@
"data": { "data": {
"id": "ChatOutput-P1jEe", "id": "ChatOutput-P1jEe",
"node": { "node": {
"base_classes": [ "base_classes": ["Text", "object", "Record", "str"],
"Text",
"object",
"Record",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -361,9 +310,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -394,10 +341,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Message to be passed as output.", "info": "Message to be passed as output.",
"input_types": [ "input_types": ["Text", "Message"],
"Text",
"Message"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -417,17 +361,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -443,9 +382,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -465,9 +402,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -505,11 +440,7 @@
"display_name": "Chat Memory", "display_name": "Chat Memory",
"id": "MemoryComponent-cdA1J", "id": "MemoryComponent-cdA1J",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": true, "beta": true,
"custom_fields": { "custom_fields": {
"n_messages": null, "n_messages": null,
@ -526,9 +457,7 @@
"field_order": [], "field_order": [],
"frozen": false, "frozen": false,
"icon": "history", "icon": "history",
"output_types": [ "output_types": ["Text"],
"Text"
],
"outputs": [ "outputs": [
{ {
"cache": true, "cache": true,
@ -537,9 +466,7 @@
"method": null, "method": null,
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -589,17 +516,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Order of the messages.", "info": "Order of the messages.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "order", "name": "order",
"options": [ "options": ["Ascending", "Descending"],
"Ascending",
"Descending"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -615,18 +537,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User", "Machine and User"],
"Machine",
"User",
"Machine and User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -642,9 +558,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -663,9 +577,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID of the chat history.", "info": "Session ID of the chat history.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -703,17 +615,10 @@
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-ODkUx", "id": "Prompt-ODkUx",
"node": { "node": {
"base_classes": [ "base_classes": ["Text", "str", "object"],
"Text",
"str",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"template": [ "template": ["context", "user_message"]
"context",
"user_message"
]
}, },
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
@ -736,9 +641,7 @@
"method": "build_prompt", "method": "build_prompt",
"name": "prompt", "name": "prompt",
"selected": "Prompt", "selected": "Prompt",
"types": [ "types": ["Prompt"],
"Prompt"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -747,9 +650,7 @@
"method": "format_prompt", "method": "format_prompt",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -781,12 +682,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -806,9 +702,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -829,12 +723,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -870,11 +759,7 @@
"data": { "data": {
"id": "OpenAIModel-9RykF", "id": "OpenAIModel-9RykF",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "object", "Text"],
"str",
"object",
"Text"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -912,9 +797,7 @@
"method": "text_response", "method": "text_response",
"name": "text_output", "name": "text_output",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -923,9 +806,7 @@
"method": "build_model", "method": "build_model",
"name": "model_output", "name": "model_output",
"selected": "BaseLanguageModel", "selected": "BaseLanguageModel",
"types": [ "types": ["BaseLanguageModel"],
"BaseLanguageModel"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -956,11 +837,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -980,9 +857,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1002,9 +877,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1024,9 +897,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1044,7 +915,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "str", "type": "str",
"value": "gpt-4o" "value": "gpt-3.5-turbo"
}, },
"openai_api_base": { "openai_api_base": {
"advanced": true, "advanced": true,
@ -1053,9 +924,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.", "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1075,9 +944,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The OpenAI API Key to use for the OpenAI model.", "info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": true, "load_from_db": true,
"multiline": false, "multiline": false,
@ -1097,9 +964,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Stream the response from the model. Streaming works only in Chat.", "info": "Stream the response from the model. Streaming works only in Chat.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1119,9 +984,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "System message to pass to the model.", "info": "System message to pass to the model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1141,9 +1004,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1179,11 +1040,7 @@
"data": { "data": {
"id": "TextOutput-vrs6T", "id": "TextOutput-vrs6T",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "object", "Text"],
"str",
"object",
"Text"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -1196,9 +1053,7 @@
"field_order": [], "field_order": [],
"frozen": false, "frozen": false,
"icon": "type", "icon": "type",
"output_types": [ "output_types": ["Text"],
"Text"
],
"template": { "template": {
"_type": "CustomComponent", "_type": "CustomComponent",
"code": { "code": {
@ -1226,10 +1081,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Text or Record to be passed as output.", "info": "Text or Record to be passed as output.",
"input_types": [ "input_types": ["Record", "Text"],
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1249,9 +1101,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,

View file

@ -8,19 +8,12 @@
"dataType": "TextInput", "dataType": "TextInput",
"id": "TextInput-sptaH", "id": "TextInput-sptaH",
"name": "text", "name": "text",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "document", "fieldName": "document",
"id": "Prompt-amqBu", "id": "Prompt-amqBu",
"inputTypes": [ "inputTypes": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -40,17 +33,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-amqBu", "id": "Prompt-amqBu",
"name": "text", "name": "text",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "TextOutput-2MS4a", "id": "TextOutput-2MS4a",
"inputTypes": [ "inputTypes": ["Record", "Text"],
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -70,18 +58,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-amqBu", "id": "Prompt-amqBu",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": ["Prompt"]
"Prompt"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-uYXZJ", "id": "OpenAIModel-uYXZJ",
"inputTypes": [ "inputTypes": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"type": "str" "type": "str"
} }
}, },
@ -101,19 +83,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-uYXZJ", "id": "OpenAIModel-uYXZJ",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "summary", "fieldName": "summary",
"id": "Prompt-gTNiz", "id": "Prompt-gTNiz",
"inputTypes": [ "inputTypes": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -133,17 +108,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-uYXZJ", "id": "OpenAIModel-uYXZJ",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-EJkG3", "id": "ChatOutput-EJkG3",
"inputTypes": [ "inputTypes": ["Text", "Message"],
"Text",
"Message"
],
"type": "str" "type": "str"
} }
}, },
@ -163,17 +133,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-gTNiz", "id": "Prompt-gTNiz",
"name": "text", "name": "text",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "TextOutput-MUDOR", "id": "TextOutput-MUDOR",
"inputTypes": [ "inputTypes": ["Record", "Text"],
"Record",
"Text"
],
"type": "str" "type": "str"
} }
}, },
@ -193,18 +158,12 @@
"dataType": "Prompt", "dataType": "Prompt",
"id": "Prompt-gTNiz", "id": "Prompt-gTNiz",
"name": "prompt", "name": "prompt",
"output_types": [ "output_types": ["Prompt"]
"Prompt"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "OpenAIModel-XawYB", "id": "OpenAIModel-XawYB",
"inputTypes": [ "inputTypes": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"type": "str" "type": "str"
} }
}, },
@ -224,17 +183,12 @@
"dataType": "OpenAIModel", "dataType": "OpenAIModel",
"id": "OpenAIModel-XawYB", "id": "OpenAIModel-XawYB",
"name": "text_output", "name": "text_output",
"output_types": [ "output_types": ["Text"]
"Text"
]
}, },
"targetHandle": { "targetHandle": {
"fieldName": "input_value", "fieldName": "input_value",
"id": "ChatOutput-DNmvg", "id": "ChatOutput-DNmvg",
"inputTypes": [ "inputTypes": ["Text", "Message"],
"Text",
"Message"
],
"type": "str" "type": "str"
} }
}, },
@ -255,16 +209,10 @@
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-amqBu", "id": "Prompt-amqBu",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "str", "Text"],
"object",
"str",
"Text"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"template": [ "template": ["document"]
"document"
]
}, },
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
@ -287,9 +235,7 @@
"method": "build_prompt", "method": "build_prompt",
"name": "prompt", "name": "prompt",
"selected": "Prompt", "selected": "Prompt",
"types": [ "types": ["Prompt"],
"Prompt"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -298,9 +244,7 @@
"method": "format_prompt", "method": "format_prompt",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -332,12 +276,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -357,9 +296,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -397,16 +334,10 @@
"display_name": "Prompt", "display_name": "Prompt",
"id": "Prompt-gTNiz", "id": "Prompt-gTNiz",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "str", "Text"],
"object",
"str",
"Text"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"template": [ "template": ["summary"]
"summary"
]
}, },
"description": "Create a prompt template with dynamic variables.", "description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt", "display_name": "Prompt",
@ -429,9 +360,7 @@
"method": "build_prompt", "method": "build_prompt",
"name": "prompt", "name": "prompt",
"selected": "Prompt", "selected": "Prompt",
"types": [ "types": ["Prompt"],
"Prompt"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -440,9 +369,7 @@
"method": "format_prompt", "method": "format_prompt",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -474,12 +401,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Document", "Message", "Record", "Text"],
"Document",
"Message",
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -499,9 +421,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -533,12 +453,7 @@
"data": { "data": {
"id": "ChatOutput-EJkG3", "id": "ChatOutput-EJkG3",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "Record", "Text", "str"],
"object",
"Record",
"Text",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -563,9 +478,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -596,10 +509,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Message to be passed as output.", "info": "Message to be passed as output.",
"input_types": [ "input_types": ["Text", "Message"],
"Text",
"Message"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -619,17 +529,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -645,9 +550,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -667,9 +570,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -701,12 +602,7 @@
"data": { "data": {
"id": "ChatOutput-DNmvg", "id": "ChatOutput-DNmvg",
"node": { "node": {
"base_classes": [ "base_classes": ["object", "Record", "Text", "str"],
"object",
"Record",
"Text",
"str"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -731,9 +627,7 @@
"method": "message_response", "method": "message_response",
"name": "message", "name": "message",
"selected": "Message", "selected": "Message",
"types": [ "types": ["Message"],
"Message"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -764,10 +658,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Message to be passed as output.", "info": "Message to be passed as output.",
"input_types": [ "input_types": ["Text", "Message"],
"Text",
"Message"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -787,17 +678,12 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Type of sender.", "info": "Type of sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
"name": "sender", "name": "sender",
"options": [ "options": ["Machine", "User"],
"Machine",
"User"
],
"password": false, "password": false,
"placeholder": "", "placeholder": "",
"required": false, "required": false,
@ -813,9 +699,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Name of the sender.", "info": "Name of the sender.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -835,9 +719,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Session ID for the message.", "info": "Session ID for the message.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -868,11 +750,7 @@
"data": { "data": {
"id": "TextInput-sptaH", "id": "TextInput-sptaH",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -893,9 +771,7 @@
"method": "text_response", "method": "text_response",
"name": "text", "name": "text",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -926,10 +802,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Text to be passed as input.", "info": "Text to be passed as input.",
"input_types": [ "input_types": ["Text"],
"Data",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -965,11 +838,7 @@
"data": { "data": {
"id": "TextOutput-2MS4a", "id": "TextOutput-2MS4a",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -982,9 +851,7 @@
"field_order": [], "field_order": [],
"frozen": false, "frozen": false,
"icon": "type", "icon": "type",
"output_types": [ "output_types": ["Text"],
"Text"
],
"template": { "template": {
"_type": "CustomComponent", "_type": "CustomComponent",
"code": { "code": {
@ -1012,10 +879,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Text or Record to be passed as output.", "info": "Text or Record to be passed as output.",
"input_types": [ "input_types": ["Record", "Text"],
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1035,9 +899,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -1073,11 +935,7 @@
"data": { "data": {
"id": "OpenAIModel-uYXZJ", "id": "OpenAIModel-uYXZJ",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -1115,9 +973,7 @@
"method": "text_response", "method": "text_response",
"name": "text_output", "name": "text_output",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -1126,9 +982,7 @@
"method": "build_model", "method": "build_model",
"name": "model_output", "name": "model_output",
"selected": "BaseLanguageModel", "selected": "BaseLanguageModel",
"types": [ "types": ["BaseLanguageModel"],
"BaseLanguageModel"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -1159,11 +1013,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1183,9 +1033,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1205,9 +1053,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1227,9 +1073,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1247,7 +1091,7 @@
"show": true, "show": true,
"title_case": false, "title_case": false,
"type": "str", "type": "str",
"value": "gpt-4o" "value": "gpt-3.5-turbo"
}, },
"openai_api_base": { "openai_api_base": {
"advanced": true, "advanced": true,
@ -1256,9 +1100,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.", "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1278,9 +1120,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The OpenAI API Key to use for the OpenAI model.", "info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": true, "load_from_db": true,
"multiline": false, "multiline": false,
@ -1300,9 +1140,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Stream the response from the model. Streaming works only in Chat.", "info": "Stream the response from the model. Streaming works only in Chat.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1322,9 +1160,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "System message to pass to the model.", "info": "System message to pass to the model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1344,9 +1180,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1382,11 +1216,7 @@
"data": { "data": {
"id": "TextOutput-MUDOR", "id": "TextOutput-MUDOR",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -1399,9 +1229,7 @@
"field_order": [], "field_order": [],
"frozen": false, "frozen": false,
"icon": "type", "icon": "type",
"output_types": [ "output_types": ["Text"],
"Text"
],
"template": { "template": {
"_type": "CustomComponent", "_type": "CustomComponent",
"code": { "code": {
@ -1429,10 +1257,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Text or Record to be passed as output.", "info": "Text or Record to be passed as output.",
"input_types": [ "input_types": ["Record", "Text"],
"Record",
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1452,9 +1277,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.", "info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": true, "multiline": true,
@ -1490,11 +1313,7 @@
"data": { "data": {
"id": "OpenAIModel-XawYB", "id": "OpenAIModel-XawYB",
"node": { "node": {
"base_classes": [ "base_classes": ["str", "Text", "object"],
"str",
"Text",
"object"
],
"beta": false, "beta": false,
"custom_fields": { "custom_fields": {
"input_value": null, "input_value": null,
@ -1532,9 +1351,7 @@
"method": "text_response", "method": "text_response",
"name": "text_output", "name": "text_output",
"selected": "Text", "selected": "Text",
"types": [ "types": ["Text"],
"Text"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
}, },
{ {
@ -1543,9 +1360,7 @@
"method": "build_model", "method": "build_model",
"name": "model_output", "name": "model_output",
"selected": "BaseLanguageModel", "selected": "BaseLanguageModel",
"types": [ "types": ["BaseLanguageModel"],
"BaseLanguageModel"
],
"value": "__UNDEFINED__" "value": "__UNDEFINED__"
} }
], ],
@ -1576,11 +1391,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text", "Data", "Prompt"],
"Text",
"Data",
"Prompt"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1600,9 +1411,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1622,9 +1431,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1644,9 +1451,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": true, "list": true,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1673,9 +1478,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.", "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1695,9 +1498,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "The OpenAI API Key to use for the OpenAI model.", "info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": true, "load_from_db": true,
"multiline": false, "multiline": false,
@ -1717,9 +1518,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "Stream the response from the model. Streaming works only in Chat.", "info": "Stream the response from the model. Streaming works only in Chat.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1739,9 +1538,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "System message to pass to the model.", "info": "System message to pass to the model.",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,
@ -1761,9 +1558,7 @@
"fileTypes": [], "fileTypes": [],
"file_path": "", "file_path": "",
"info": "", "info": "",
"input_types": [ "input_types": ["Text"],
"Text"
],
"list": false, "list": false,
"load_from_db": false, "load_from_db": false,
"multiline": false, "multiline": false,

File diff suppressed because one or more lines are too long

View file

@ -160,7 +160,7 @@ async def build_custom_component(params: dict, custom_component: "CustomComponen
if raw is None and isinstance(build_result, (dict, Data, str)): if raw is None and isinstance(build_result, (dict, Data, str)):
raw = build_result.data if isinstance(build_result, Data) else build_result raw = build_result.data if isinstance(build_result, Data) else build_result
artifact_type = get_artifact_type(custom_component.repr_value or raw, build_result) artifact_type = get_artifact_type(custom_component or raw, build_result)
raw = post_process_raw(raw, artifact_type) raw = post_process_raw(raw, artifact_type)
artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type} artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type}
return custom_component, build_result, artifact return custom_component, build_result, artifact

View file

@ -1,14 +1,13 @@
import warnings
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Annotated, Coroutine, Optional, Union from typing import Annotated, Coroutine, Optional, Union
from uuid import UUID from uuid import UUID
import warnings
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from fastapi import Depends, HTTPException, Security, status from fastapi import Depends, HTTPException, Security, status
from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer
from jose import JWTError, jwt from jose import JWTError, jwt
from loguru import logger
from sqlmodel import Session from sqlmodel import Session
from starlette.websockets import WebSocket from starlette.websockets import WebSocket
@ -92,44 +91,58 @@ async def get_current_user_by_jwt(
) -> User: ) -> User:
settings_service = get_settings_service() settings_service = get_settings_service()
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
if isinstance(token, Coroutine): if isinstance(token, Coroutine):
token = await token token = await token
if settings_service.auth_settings.SECRET_KEY.get_secret_value() is None: secret_key = settings_service.auth_settings.SECRET_KEY.get_secret_value()
raise credentials_exception if secret_key is None:
logger.error("Secret key is not set in settings.")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
# Careful not to leak sensitive information
detail="Authentication failure: Verify authentication settings.",
headers={"WWW-Authenticate": "Bearer"},
)
try: try:
# Ignore warning about datetime.utcnow
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
payload = jwt.decode(token, secret_key, algorithms=[settings_service.auth_settings.ALGORITHM])
payload = jwt.decode( user_id: UUID = payload.get("sub")
token, token_type: str = payload.get("type")
settings_service.auth_settings.SECRET_KEY.get_secret_value(),
algorithms=[settings_service.auth_settings.ALGORITHM],
)
user_id: UUID = payload.get("sub") # type: ignore
token_type: str = payload.get("type") # type: ignore
if expires := payload.get("exp", None): if expires := payload.get("exp", None):
expires_datetime = datetime.fromtimestamp(expires, timezone.utc) expires_datetime = datetime.fromtimestamp(expires, timezone.utc)
# TypeError: can't compare offset-naive and offset-aware datetimes
if datetime.now(timezone.utc) > expires_datetime: if datetime.now(timezone.utc) > expires_datetime:
raise credentials_exception logger.info("Token expired for user")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token has expired.",
headers={"WWW-Authenticate": "Bearer"},
)
if user_id is None or token_type: if user_id is None or token_type:
raise credentials_exception logger.info(f"Invalid token payload. Token type: {token_type}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token details.",
headers={"WWW-Authenticate": "Bearer"},
)
except JWTError as e: except JWTError as e:
raise credentials_exception from e logger.error(f"JWT decoding error: {e}")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
) from e
user = get_user_by_id(db, user_id) # type: ignore user = get_user_by_id(db, user_id)
if user is None or not user.is_active: if user is None or not user.is_active:
raise credentials_exception logger.info("User not found or inactive.")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found or is inactive.",
headers={"WWW-Authenticate": "Bearer"},
)
return user return user

View file

@ -6,24 +6,21 @@ from typing import TYPE_CHECKING
import sqlalchemy as sa import sqlalchemy as sa
from alembic import command, util from alembic import command, util
from alembic.config import Config from alembic.config import Config
from loguru import logger
from sqlalchemy import inspect
from sqlalchemy.exc import OperationalError
from sqlalchemy.engine import Engine
from sqlalchemy import event
from sqlmodel import Session, SQLModel, create_engine, select, text
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.database import models # noqa from langflow.services.database import models # noqa
from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults from langflow.services.database.utils import Result, TableResults
from langflow.services.deps import get_settings_service from langflow.services.deps import get_settings_service
from langflow.services.utils import teardown_superuser from langflow.services.utils import teardown_superuser
from loguru import logger
from sqlalchemy import event, inspect
from sqlalchemy.engine import Engine
from sqlalchemy.exc import OperationalError
from sqlmodel import Session, SQLModel, create_engine, select, text
if TYPE_CHECKING: if TYPE_CHECKING:
from sqlalchemy.engine import Engine
from langflow.services.settings.service import SettingsService from langflow.services.settings.service import SettingsService
from sqlalchemy.engine import Engine
class DatabaseService(Service): class DatabaseService(Service):
@ -48,12 +45,23 @@ class DatabaseService(Service):
connect_args = {"check_same_thread": False} connect_args = {"check_same_thread": False}
else: else:
connect_args = {} connect_args = {}
try:
return create_engine( return create_engine(
self.database_url, self.database_url,
connect_args=connect_args, connect_args=connect_args,
pool_size=self.settings_service.settings.pool_size, pool_size=self.settings_service.settings.pool_size,
max_overflow=self.settings_service.settings.max_overflow, max_overflow=self.settings_service.settings.max_overflow,
) )
except sa.exc.NoSuchModuleError as exc:
# sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
if "postgres" in str(exc) and not self.database_url.startswith("postgresql"):
# https://stackoverflow.com/questions/62688256/sqlalchemy-exc-nosuchmoduleerror-cant-load-plugin-sqlalchemy-dialectspostgre
self.database_url = self.database_url.replace("postgres://", "postgresql://")
logger.warning(
"Fixed postgres dialect in database URL. Replacing postgres:// with postgresql://. To avoid this warning, update the database URL."
)
return self._create_engine()
raise RuntimeError("Error creating database engine") from exc
@event.listens_for(Engine, "connect") @event.listens_for(Engine, "connect")
def on_connection(dbapi_connection, connection_record): def on_connection(dbapi_connection, connection_record):

View file

@ -91,9 +91,17 @@ class MessageModel(DefaultModel):
files: list[str] = [] files: list[str] = []
@field_validator("files", mode="before") @field_validator("files", mode="before")
@classmethod
def validate_files(cls, v): def validate_files(cls, v):
if isinstance(v, str): if isinstance(v, str):
return json.loads(v) v = json.loads(v)
return v
@field_serializer("files")
@classmethod
def serialize_files(cls, v):
if isinstance(v, list):
return json.dumps(v)
return v return v
@classmethod @classmethod

View file

@ -3,12 +3,11 @@ from pathlib import Path
from typing import TYPE_CHECKING, List, Optional, Union from typing import TYPE_CHECKING, List, Optional, Union
import duckdb import duckdb
from loguru import logger
from platformdirs import user_cache_dir
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.monitor.schema import MessageModel, TransactionModel, VertexBuildModel from langflow.services.monitor.schema import MessageModel, TransactionModel, VertexBuildModel
from langflow.services.monitor.utils import add_row_to_table, drop_and_create_table_if_schema_mismatch from langflow.services.monitor.utils import add_row_to_table, drop_and_create_table_if_schema_mismatch
from loguru import logger
from platformdirs import user_cache_dir
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.settings.manager import SettingsService from langflow.services.settings.manager import SettingsService
@ -141,7 +140,7 @@ class MonitorService(Service):
order: Optional[str] = "DESC", order: Optional[str] = "DESC",
limit: Optional[int] = None, limit: Optional[int] = None,
): ):
query = "SELECT index, flow_id, sender_name, sender, session_id, text, timestamp FROM messages" query = "SELECT index, flow_id, sender_name, sender, session_id, text, files, timestamp FROM messages"
conditions = [] conditions = []
if sender: if sender:
conditions.append(f"sender = '{sender}'") conditions.append(f"sender = '{sender}'")

View file

@ -1158,22 +1158,25 @@ files = [
[[package]] [[package]]
name = "langchain" name = "langchain"
version = "0.2.3" version = "0.2.4"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"}, {file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"},
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"}, {file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"},
] ]
[package.dependencies] [package.dependencies]
aiohttp = ">=3.8.3,<4.0.0" aiohttp = ">=3.8.3,<4.0.0"
async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""}
langchain-core = ">=0.2.0,<0.3.0" langchain-core = ">=0.2.6,<0.3.0"
langchain-text-splitters = ">=0.2.0,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0"
langsmith = ">=0.1.17,<0.2.0" langsmith = ">=0.1.17,<0.2.0"
numpy = ">=1,<2" numpy = [
{version = ">=1,<2", markers = "python_version < \"3.12\""},
{version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""},
]
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
requests = ">=2,<3" requests = ">=2,<3"
@ -1205,19 +1208,19 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]] [[package]]
name = "langchain-core" name = "langchain-core"
version = "0.2.5" version = "0.2.6"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"}, {file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"},
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"}, {file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"},
] ]
[package.dependencies] [package.dependencies]
jsonpatch = ">=1.33,<2.0" jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.1.66,<0.2.0" langsmith = ">=0.1.75,<0.2.0"
packaging = ">=23.2,<24.0" packaging = ">=23.2,<25"
pydantic = ">=1,<3" pydantic = ">=1,<3"
PyYAML = ">=5.3" PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0" tenacity = ">=8.1.0,<9.0.0"
@ -1859,13 +1862,13 @@ files = [
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "23.2" version = "24.1"
description = "Core utilities for Python packages" description = "Core utilities for Python packages"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
] ]
[[package]] [[package]]

View file

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

File diff suppressed because it is too large Load diff

View file

@ -85,9 +85,6 @@
"format": "npx prettier --write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore", "format": "npx prettier --write \"{tests,src}/**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore",
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite" "type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
}, },
"simple-git-hooks": {
"pre-commit": "npx pretty-quick --staged"
},
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"react-app", "react-app",
@ -130,7 +127,6 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"prettier-plugin-organize-imports": "^3.2.3", "prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.3.0", "prettier-plugin-tailwindcss": "^0.3.0",
"pretty-quick": "^3.1.3",
"simple-git-hooks": "^2.11.1", "simple-git-hooks": "^2.11.1",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"tailwindcss-dotted-background": "^1.1.0", "tailwindcss-dotted-background": "^1.1.0",

View file

@ -18,13 +18,13 @@ import { autoLogin, getGlobalVariables, getHealth } from "./controllers/API";
import { setupAxiosDefaults } from "./controllers/API/utils"; import { setupAxiosDefaults } from "./controllers/API/utils";
import useTrackLastVisitedPath from "./hooks/use-track-last-visited-path"; import useTrackLastVisitedPath from "./hooks/use-track-last-visited-path";
import Router from "./routes"; import Router from "./routes";
import { Case } from "./shared/components/caseComponent";
import useAlertStore from "./stores/alertStore"; import useAlertStore from "./stores/alertStore";
import { useDarkStore } from "./stores/darkStore"; import { useDarkStore } from "./stores/darkStore";
import useFlowsManagerStore from "./stores/flowsManagerStore"; import useFlowsManagerStore from "./stores/flowsManagerStore";
import { useFolderStore } from "./stores/foldersStore"; import { useFolderStore } from "./stores/foldersStore";
import { useGlobalVariablesStore } from "./stores/globalVariablesStore/globalVariables"; import { useGlobalVariablesStore } from "./stores/globalVariablesStore/globalVariables";
import { useStoreStore } from "./stores/storeStore"; import { useStoreStore } from "./stores/storeStore";
import { useTypesStore } from "./stores/typesStore";
export default function App() { export default function App() {
useTrackLastVisitedPath(); useTrackLastVisitedPath();
@ -43,10 +43,8 @@ export default function App() {
const { isAuthenticated, login, setUserData, setAutoLogin, getUser } = const { isAuthenticated, login, setUserData, setAutoLogin, getUser } =
useContext(AuthContext); useContext(AuthContext);
const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows);
const setLoading = useAlertStore((state) => state.setLoading); const setLoading = useAlertStore((state) => state.setLoading);
const fetchApiData = useStoreStore((state) => state.fetchApiData); const fetchApiData = useStoreStore((state) => state.fetchApiData);
const getTypes = useTypesStore((state) => state.getTypes);
const refreshVersion = useDarkStore((state) => state.refreshVersion); const refreshVersion = useDarkStore((state) => state.refreshVersion);
const refreshStars = useDarkStore((state) => state.refreshStars); const refreshStars = useDarkStore((state) => state.refreshStars);
const setGlobalVariables = useGlobalVariablesStore( const setGlobalVariables = useGlobalVariablesStore(
@ -56,8 +54,7 @@ export default function App() {
const navigate = useNavigate(); const navigate = useNavigate();
const dark = useDarkStore((state) => state.dark); const dark = useDarkStore((state) => state.dark);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
const loadingFolders = useFolderStore((state) => state.loading);
const [isLoadingHealth, setIsLoadingHealth] = useState(false); const [isLoadingHealth, setIsLoadingHealth] = useState(false);
@ -80,7 +77,6 @@ export default function App() {
login(user["access_token"]); login(user["access_token"]);
setUserData(user); setUserData(user);
setAutoLogin(true); setAutoLogin(true);
setLoading(false);
fetchAllData(); fetchAllData();
} }
}) })
@ -116,13 +112,13 @@ export default function App() {
if (isAuthenticated) { if (isAuthenticated) {
try { try {
await setupAxiosDefaults(); await setupAxiosDefaults();
await getFoldersApi();
await getTypes();
await refreshFlows();
const res = await getGlobalVariables(); const res = await getGlobalVariables();
setGlobalVariables(res); setGlobalVariables(res);
checkHasStore(); checkHasStore();
fetchApiData(); fetchApiData();
resolve(); resolve();
} catch (error) { } catch (error) {
console.error("Failed to fetch data:", error); console.error("Failed to fetch data:", error);
@ -175,6 +171,8 @@ export default function App() {
} }
}; };
const isLoadingApplication = isLoading || isLoadingFolders;
return ( return (
//need parent component with width and height //need parent component with width and height
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
@ -197,15 +195,15 @@ export default function App() {
></FetchErrorComponent> ></FetchErrorComponent>
} }
{isLoading || loadingFolders ? ( <Case condition={isLoadingApplication}>
<div className="loading-page-panel"> <div className="loading-page-panel">
<LoadingComponent remSize={50} /> <LoadingComponent remSize={50} />
</div> </div>
) : ( </Case>
<>
<Case condition={!isLoadingApplication}>
<Router /> <Router />
</> </Case>
)}
</> </>
</ErrorBoundary> </ErrorBoundary>
<div></div> <div></div>

View file

@ -1,6 +1,5 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { ReactNode, useEffect, useRef, useState } from "react"; import { ReactNode, useEffect, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import CodeAreaComponent from "../../../../components/codeAreaComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent";
import DictComponent from "../../../../components/dictComponent"; import DictComponent from "../../../../components/dictComponent";
@ -18,7 +17,10 @@ import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleShadComponent from "../../../../components/toggleShadComponent"; import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button"; import { Button } from "../../../../components/ui/button";
import { RefreshButton } from "../../../../components/ui/refreshButton"; import { RefreshButton } from "../../../../components/ui/refreshButton";
import { LANGFLOW_SUPPORTED_TYPES } from "../../../../constants/constants"; import {
LANGFLOW_SUPPORTED_TYPES,
TOOLTIP_EMPTY,
} from "../../../../constants/constants";
import { Case } from "../../../../shared/components/caseComponent"; import { Case } from "../../../../shared/components/caseComponent";
import useFlowStore from "../../../../stores/flowStore"; import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
@ -49,8 +51,10 @@ import useHandleNodeClass from "../../../hooks/use-handle-node-class";
import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons"; import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons";
import HandleTooltips from "../HandleTooltipComponent"; import HandleTooltips from "../HandleTooltipComponent";
import OutputComponent from "../OutputComponent"; import OutputComponent from "../OutputComponent";
import OutputModal from "../outputModal"; import TooltipRenderComponent from "../tooltipRenderComponent";
import { TEXT_FIELD_TYPES } from "./constants"; import { TEXT_FIELD_TYPES } from "./constants";
import OutputModal from "../outputModal";
import { useHotkeys } from "react-hotkeys-hook";
export default function ParameterComponent({ export default function ParameterComponent({
left, left,
@ -71,6 +75,8 @@ export default function ParameterComponent({
selected, selected,
outputProxy, outputProxy,
}: ParameterComponentType): JSX.Element { }: ParameterComponentType): JSX.Element {
const ref = useRef<HTMLDivElement>(null);
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
const infoHtml = useRef<HTMLDivElement & ReactNode>(null); const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
@ -81,13 +87,16 @@ export default function ParameterComponent({
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
const flow = currentFlow?.data?.nodes ?? null;
const groupedEdge = useRef(null);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
const [openOutputModal, setOpenOutputModal] = useState(false); const [openOutputModal, setOpenOutputModal] = useState(false);
const flowPool = useFlowStore((state) => state.flowPool); const flowPool = useFlowStore((state) => state.flowPool);
const isValid = const displayOutputPreview =
!!flowPool[data.id] && !!flowPool[data.id] &&
flowPool[data.id][flowPool[data.id].length - 1]?.valid; flowPool[data.id][flowPool[data.id].length - 1]?.valid &&
flowPool[data.id][flowPool[data.id].length - 1]?.data?.logs[0]?.message;
const flowPoolNode = (flowPool[data.id] ?? [])[ const flowPoolNode = (flowPool[data.id] ?? [])[
(flowPool[data.id]?.length ?? 1) - 1 (flowPool[data.id]?.length ?? 1) - 1
@ -96,7 +105,6 @@ export default function ParameterComponent({
if (flowPoolNode?.data?.logs && outputName) { if (flowPoolNode?.data?.logs && outputName) {
hasOutputs = flowPoolNode?.data?.logs[outputName] ?? null; hasOutputs = flowPoolNode?.data?.logs[outputName] ?? null;
} }
const displayOutputPreview = isValid && hasOutputs;
const unknownOutput = !!( const unknownOutput = !!(
flowPool[data.id] && flowPool[data.id] &&
flowPool[data.id][flowPool[data.id].length - 1]?.data?.logs[0]?.type === flowPool[data.id][flowPool[data.id].length - 1]?.data?.logs[0]?.type ===
@ -157,7 +165,7 @@ export default function ParameterComponent({
const handleOnNewValue = async ( const handleOnNewValue = async (
newValue: string | string[] | boolean | Object[], newValue: string | string[] | boolean | Object[],
skipSnapshot: boolean | undefined = false skipSnapshot: boolean | undefined = false,
): Promise<void> => { ): Promise<void> => {
handleOnNewValueHook(newValue, skipSnapshot); handleOnNewValueHook(newValue, skipSnapshot);
}; };
@ -270,7 +278,7 @@ export default function ParameterComponent({
className={classNames( className={classNames(
left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ", left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ",
"h-3 w-3 rounded-full border-2 bg-background", "h-3 w-3 rounded-full border-2 bg-background",
!showNode ? "mt-0" : "" !showNode ? "mt-0" : "",
)} )}
style={{ style={{
borderColor: color ?? nodeColors.unknown, borderColor: color ?? nodeColors.unknown,
@ -287,6 +295,7 @@ export default function ParameterComponent({
) )
) : ( ) : (
<div <div
ref={ref}
className={ className={
"relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" + "relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" +
((name === "code" && type === "code") || ((name === "code" && type === "code") ||

View file

@ -464,10 +464,11 @@ export default function GenericNode({
.filter((templateField) => templateField.charAt(0) !== "_") .filter((templateField) => templateField.charAt(0) !== "_")
.map( .map(
(templateField: string, idx) => (templateField: string, idx) =>
data.node!.template[templateField].show && data.node!.template[templateField]?.show &&
!data.node!.template[templateField].advanced && ( !data.node!.template[templateField]?.advanced && (
<ParameterComponent <ParameterComponent
index={idx} selected={selected}
index={idx.toString()}
key={scapedJSONStringfy({ key={scapedJSONStringfy({
inputTypes: inputTypes:
data.node!.template[templateField].input_types, data.node!.template[templateField].input_types,
@ -724,10 +725,11 @@ export default function GenericNode({
.sort((a, b) => sortFields(a, b, data.node?.field_order ?? [])) .sort((a, b) => sortFields(a, b, data.node?.field_order ?? []))
.map((templateField: string, idx) => ( .map((templateField: string, idx) => (
<div key={idx}> <div key={idx}>
{data.node!.template[templateField].show && {data.node!.template[templateField]?.show &&
!data.node!.template[templateField].advanced ? ( !data.node!.template[templateField]?.advanced ? (
<ParameterComponent <ParameterComponent
index={idx} selected={selected}
index={idx.toString()}
key={scapedJSONStringfy({ key={scapedJSONStringfy({
inputTypes: inputTypes:
data.node!.template[templateField].input_types, data.node!.template[templateField].input_types,

View file

@ -5,9 +5,9 @@ export function countHandlesFn(data: NodeDataType): number {
.filter((templateField) => templateField.charAt(0) !== "_") .filter((templateField) => templateField.charAt(0) !== "_")
.map((templateCamp) => { .map((templateCamp) => {
const { template } = data.node!; const { template } = data.node!;
if (template[templateCamp].input_types) return true; if (template[templateCamp]?.input_types) return true;
if (!template[templateCamp].show) return false; if (!template[templateCamp]?.show) return false;
switch (template[templateCamp].type) { switch (template[templateCamp]?.type) {
case "str": case "str":
case "bool": case "bool":
case "float": case "float":

View file

@ -1,5 +1,9 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { useEffect } from "react"; import { useEffect } from "react";
import {
ERROR_UPDATING_COMPONENT,
TITLE_ERROR_UPDATING_COMPONENT,
} from "../../constants/constants";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import { ResponseErrorDetailAPI } from "../../types/api"; import { ResponseErrorDetailAPI } from "../../types/api";
@ -38,8 +42,10 @@ const useFetchDataOnMount = (
let responseError = error as ResponseErrorDetailAPI; let responseError = error as ResponseErrorDetailAPI;
setErrorData({ setErrorData({
title: "Error while updating the Component", title: TITLE_ERROR_UPDATING_COMPONENT,
list: [responseError?.response?.data?.detail ?? "Unknown error"], list: [
responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT,
],
}); });
} }
setIsLoading(false); setIsLoading(false);

View file

@ -1,4 +1,8 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import {
ERROR_UPDATING_COMPONENT,
TITLE_ERROR_UPDATING_COMPONENT,
} from "../../constants/constants";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import { ResponseErrorTypeAPI } from "../../types/api"; import { ResponseErrorTypeAPI } from "../../types/api";
@ -42,9 +46,10 @@ const useHandleOnNewValue = (
} catch (error) { } catch (error) {
let responseError = error as ResponseErrorTypeAPI; let responseError = error as ResponseErrorTypeAPI;
setErrorData({ setErrorData({
title: "Error while updating the Component", title: TITLE_ERROR_UPDATING_COMPONENT,
list: [ list: [
responseError?.response?.data?.detail.error ?? "Unknown error", responseError?.response?.data?.detail.error ??
ERROR_UPDATING_COMPONENT,
], ],
}); });
} }

View file

@ -1,4 +1,8 @@
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import {
ERROR_UPDATING_COMPONENT,
TITLE_ERROR_UPDATING_COMPONENT,
} from "../../constants/constants";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import { ResponseErrorDetailAPI } from "../../types/api"; import { ResponseErrorDetailAPI } from "../../types/api";
import { handleUpdateValues } from "../../utils/parameterUtils"; import { handleUpdateValues } from "../../utils/parameterUtils";
@ -25,8 +29,10 @@ const useHandleRefreshButtonPress = (setIsLoading, setNode) => {
let responseError = error as ResponseErrorDetailAPI; let responseError = error as ResponseErrorDetailAPI;
setErrorData({ setErrorData({
title: "Error while updating the Component", title: TITLE_ERROR_UPDATING_COMPONENT,
list: [responseError?.response?.data?.detail ?? "Unknown error"], list: [
responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT,
],
}); });
} }
setIsLoading(false); setIsLoading(false);

View file

@ -70,7 +70,10 @@ export default function AddNewVariableButton({
let responseError = error as ResponseErrorDetailAPI; let responseError = error as ResponseErrorDetailAPI;
setErrorData({ setErrorData({
title: "Error creating variable", title: "Error creating variable",
list: [responseError?.response?.data?.detail ?? "Unknown error"], list: [
responseError?.response?.data?.detail ??
"An unexpected error occurred while adding a new variable. Please try again.",
],
}); });
}); });
} }

View file

@ -36,6 +36,7 @@ export default function Header(): JSX.Element {
const location = useLocation(); const location = useLocation();
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext); const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const navigate = useNavigate(); const navigate = useNavigate();
const removeFlow = useFlowsManagerStore((store) => store.removeFlow); const removeFlow = useFlowsManagerStore((store) => store.removeFlow);
const hasStore = useStoreStore((state) => state.hasStore); const hasStore = useStoreStore((state) => state.hasStore);
@ -208,7 +209,7 @@ export default function Header(): JSX.Element {
0, 0,
BACKEND_URL.length - 1 BACKEND_URL.length - 1
)}${BASE_URL_API}files/profile_pictures/${ )}${BASE_URL_API}files/profile_pictures/${
userData?.profile_image ?? "Space/046-rocket.png" userData?.profile_image ?? "Space/046-rocket.svg"
}` ?? profileCircle }` ?? profileCircle
} }
className="h-7 w-7 shrink-0 focus-visible:outline-0" className="h-7 w-7 shrink-0 focus-visible:outline-0"
@ -226,7 +227,7 @@ export default function Header(): JSX.Element {
0, 0,
BACKEND_URL.length - 1 BACKEND_URL.length - 1
)}${BASE_URL_API}files/profile_pictures/${ )}${BASE_URL_API}files/profile_pictures/${
userData?.profile_image userData?.profile_image ?? "Space/046-rocket.svg"
}` ?? profileCircle }` ?? profileCircle
} }
className="h-5 w-5 focus-visible:outline-0 " className="h-5 w-5 focus-visible:outline-0 "

View file

@ -31,7 +31,7 @@ export default function InputListComponent({
<div <div
className={classNames( className={classNames(
value.length > 1 && editNode ? "my-1" : "", value.length > 1 && editNode ? "my-1" : "",
"flex flex-col gap-3", "flex flex-col gap-3"
)} )}
> >
{value.map((singleValue, idx) => { {value.map((singleValue, idx) => {

View file

@ -37,8 +37,8 @@ const SideBarFoldersButtonsComponent = ({
const currentFolder = pathname.split("/"); const currentFolder = pathname.split("/");
const urlWithoutPath = pathname.split("/").length < 4; const urlWithoutPath = pathname.split("/").length < 4;
const myCollectionId = useFolderStore((state) => state.myCollectionId); const myCollectionId = useFolderStore((state) => state.myCollectionId);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const folderIdDragging = useFolderStore((state) => state.folderIdDragging); const folderIdDragging = useFolderStore((state) => state.folderIdDragging);
const refreshFolders = useFolderStore((state) => state.refreshFolders);
const checkPathName = (itemId: string) => { const checkPathName = (itemId: string) => {
if (urlWithoutPath && itemId === myCollectionId) { if (urlWithoutPath && itemId === myCollectionId) {
@ -85,7 +85,7 @@ const SideBarFoldersButtonsComponent = ({
function addNewFolder() { function addNewFolder() {
addFolder({ name: "New Folder", parent_id: null, description: "" }).then( addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
(res) => { (res) => {
getFoldersApi(true); refreshFolders();
} }
); );
} }

View file

@ -16,7 +16,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
); );
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const refreshFolders = useFolderStore((state) => state.refreshFolders);
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const triggerFolderChange = (folderId) => { const triggerFolderChange = (folderId) => {
@ -118,7 +118,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
setFolderIdDragging(""); setFolderIdDragging("");
updateFlowInDatabase(updatedFlow).then(() => { updateFlowInDatabase(updatedFlow).then(() => {
getFoldersApi(true); refreshFolders();
triggerFolderChange(folderId); triggerFolderChange(folderId);
}); });
}; };
@ -129,7 +129,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
setFolderDragging(false); setFolderDragging(false);
setFolderIdDragging(""); setFolderIdDragging("");
uploadFlowsFromFolders(formData).then(() => { uploadFlowsFromFolders(formData).then(() => {
getFoldersApi(true); refreshFolders();
triggerFolderChange(folderId); triggerFolderChange(folderId);
}); });
}; };

View file

@ -28,7 +28,7 @@ export default function SidebarNav({
}: SidebarNavProps) { }: SidebarNavProps) {
const location = useLocation(); const location = useLocation();
const pathname = location.pathname; const pathname = location.pathname;
const loadingFolders = useFolderStore((state) => state.loading); const loadingFolders = useFolderStore((state) => state.isLoadingFolders);
const folders = useFolderStore((state) => state.folders); const folders = useFolderStore((state) => state.folders);
const pathValues = ["folder", "components", "flows", "all"]; const pathValues = ["folder", "components", "flows", "all"];

View file

@ -105,7 +105,7 @@ const TableComponent = forwardRef<
} }
}, 50); }, 50);
setTimeout(() => { setTimeout(() => {
realRef.current.api.hideOverlay(); realRef?.current?.api?.hideOverlay();
}, 1000); }, 1000);
if (props.onGridReady) props.onGridReady(params); if (props.onGridReady) props.onGridReady(params);
}; };

View file

@ -853,3 +853,8 @@ export const ALLOWED_IMAGE_INPUT_EXTENSIONS = ["png", "jpg", "jpeg"];
export const FS_ERROR_TEXT = export const FS_ERROR_TEXT =
"Please ensure your file has one of the following extensions:"; "Please ensure your file has one of the following extensions:";
export const SN_ERROR_TEXT = ALLOWED_IMAGE_INPUT_EXTENSIONS.join(", "); export const SN_ERROR_TEXT = ALLOWED_IMAGE_INPUT_EXTENSIONS.join(", ");
export const ERROR_UPDATING_COMPONENT =
"An unexpected error occurred while updating the Component. Please try again.";
export const TITLE_ERROR_UPDATING_COMPONENT =
"Error while updating the Component";

View file

@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom";
import Cookies from "universal-cookie"; import Cookies from "universal-cookie";
import { getLoggedUser, requestLogout } from "../controllers/API"; import { getLoggedUser, requestLogout } from "../controllers/API";
import useAlertStore from "../stores/alertStore"; import useAlertStore from "../stores/alertStore";
import { useFolderStore } from "../stores/foldersStore";
import { Users } from "../types/api"; import { Users } from "../types/api";
import { AuthContextType } from "../types/contexts/auth"; import { AuthContextType } from "../types/contexts/auth";
@ -43,6 +44,8 @@ export function AuthProvider({ children }): React.ReactElement {
cookies.get("apikey_tkn_lflw") cookies.get("apikey_tkn_lflw")
); );
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
useEffect(() => { useEffect(() => {
const storedAccessToken = cookies.get("access_token_lf"); const storedAccessToken = cookies.get("access_token_lf");
if (storedAccessToken) { if (storedAccessToken) {
@ -59,11 +62,12 @@ export function AuthProvider({ children }): React.ReactElement {
function getUser() { function getUser() {
getLoggedUser() getLoggedUser()
.then((user) => { .then(async (user) => {
setUserData(user); setUserData(user);
setLoading(false);
const isSuperUser = user!.is_superuser; const isSuperUser = user!.is_superuser;
setIsAdmin(isSuperUser); setIsAdmin(isSuperUser);
getFoldersApi(true, true);
}) })
.catch((error) => { .catch((error) => {
setLoading(false); setLoading(false);

View file

@ -87,6 +87,7 @@ function ApiInterceptor() {
if (!checkRequest) { if (!checkRequest) {
controller.abort("Duplicate Request"); controller.abort("Duplicate Request");
console.error("Duplicate Request");
} }
const accessToken = cookies.get("access_token_lf"); const accessToken = cookies.get("access_token_lf");

View file

@ -4,9 +4,10 @@ export function checkDuplicateRequestAndStoreRequest(config) {
const lastUrl = localStorage.getItem("lastUrlCalled"); const lastUrl = localStorage.getItem("lastUrlCalled");
const lastMethodCalled = localStorage.getItem("lastMethodCalled"); const lastMethodCalled = localStorage.getItem("lastMethodCalled");
const lastRequestTime = localStorage.getItem("lastRequestTime"); const lastRequestTime = localStorage.getItem("lastRequestTime");
const lastCurrentUrl = localStorage.getItem("lastCurrentUrl");
const currentUrl = window.location.pathname;
const currentTime = Date.now(); const currentTime = Date.now();
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) => const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
config?.url!.includes(request) config?.url!.includes(request)
); );
@ -17,7 +18,8 @@ export function checkDuplicateRequestAndStoreRequest(config) {
lastMethodCalled === config.method && lastMethodCalled === config.method &&
lastMethodCalled === "get" && // Assuming you want to check only for GET requests lastMethodCalled === "get" && // Assuming you want to check only for GET requests
lastRequestTime && lastRequestTime &&
currentTime - parseInt(lastRequestTime, 10) < 800 currentTime - parseInt(lastRequestTime, 10) < 300 &&
lastCurrentUrl === currentUrl
) { ) {
return false; return false;
} }
@ -25,6 +27,7 @@ export function checkDuplicateRequestAndStoreRequest(config) {
localStorage.setItem("lastUrlCalled", config.url ?? ""); localStorage.setItem("lastUrlCalled", config.url ?? "");
localStorage.setItem("lastMethodCalled", config.method ?? ""); localStorage.setItem("lastMethodCalled", config.method ?? "");
localStorage.setItem("lastRequestTime", currentTime.toString()); localStorage.setItem("lastRequestTime", currentTime.toString());
localStorage.setItem("lastCurrentUrl", currentUrl);
return true; return true;
} }

View file

@ -1094,8 +1094,6 @@ export async function getMessagesTable(
const rowsOrganized = rows.data; const rowsOrganized = rows.data;
console.log(rowsOrganized);
const columns = extractColumnsFromRows(rowsOrganized, mode, excludedFields); const columns = extractColumnsFromRows(rowsOrganized, mode, excludedFields);
const sessions = new Set<string>(); const sessions = new Set<string>();
rowsOrganized.forEach((row) => { rowsOrganized.forEach((row) => {

View file

@ -5,6 +5,7 @@ import CsvOutputComponent from "../../../../components/csvOutputComponent";
import DataOutputComponent from "../../../../components/dataOutputComponent"; import DataOutputComponent from "../../../../components/dataOutputComponent";
import InputListComponent from "../../../../components/inputListComponent"; import InputListComponent from "../../../../components/inputListComponent";
import PdfViewer from "../../../../components/pdfViewer"; import PdfViewer from "../../../../components/pdfViewer";
import RecordsOutputComponent from "../../../../components/recordsOutputComponent";
import { Textarea } from "../../../../components/ui/textarea"; import { Textarea } from "../../../../components/ui/textarea";
import { PDFViewConstant } from "../../../../constants/constants"; import { PDFViewConstant } from "../../../../constants/constants";
import { InputOutput } from "../../../../constants/enums"; import { InputOutput } from "../../../../constants/enums";
@ -253,7 +254,7 @@ export default function IOFieldView({
rows={ rows={
Array.isArray(flowPoolNode?.data?.artifacts) Array.isArray(flowPoolNode?.data?.artifacts)
? flowPoolNode?.data?.artifacts?.map( ? flowPoolNode?.data?.artifacts?.map(
(artifact) => artifact.data (artifact) => artifact.data,
) ?? [] ) ?? []
: [flowPoolNode?.data?.artifacts] : [flowPoolNode?.data?.artifacts]
} }

View file

@ -18,7 +18,7 @@ export default function SessionView({ rows }: { rows: Array<any> }) {
setSelectedRows, setSelectedRows,
setSuccessData, setSuccessData,
setErrorData, setErrorData,
selectedRows, selectedRows
); );
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData); const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);

View file

@ -36,12 +36,6 @@ export default function ChatView({
const outputTypes = outputs.map((obj) => obj.type); const outputTypes = outputs.map((obj) => obj.type);
const updateFlowPool = useFlowStore((state) => state.updateFlowPool); const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
// useEffect(() => {
// if (!outputTypes.includes("ChatOutput")) {
// setNoticeData({ title: NOCHATOUTPUT_NOTICE_ALERT });
// }
// }, []);
//build chat history //build chat history
useEffect(() => { useEffect(() => {
const chatOutputResponses: VertexBuildTypeAPI[] = []; const chatOutputResponses: VertexBuildTypeAPI[] = [];
@ -62,14 +56,24 @@ export default function ChatView({
const chatMessages: ChatMessageType[] = chatOutputResponses const chatMessages: ChatMessageType[] = chatOutputResponses
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)) .sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp))
// //
.filter((output) => output.data.message) .filter(
(output) =>
output.data.message || (!output.data.message && output.artifacts)
)
.map((output, index) => { .map((output, index) => {
try { try {
console.log("output:", output); console.log("output:", output);
const messageOutput = output.data.message;
const hasMessageValue =
messageOutput?.message ||
messageOutput?.message === "" ||
(messageOutput?.files ?? []).length > 0 ||
messageOutput?.stream_url;
const { sender, message, sender_name, stream_url, files } = const { sender, message, sender_name, stream_url, files } =
output.data.message; hasMessageValue ? output.data.message : output.artifacts;
console.log("output.data.message:", output.data.message);
console.log("output.data.message.files:", output.data.message.files);
const is_ai = const is_ai =
sender === "Machine" || sender === null || sender === undefined; sender === "Machine" || sender === null || sender === undefined;
return { return {
@ -136,26 +140,12 @@ export default function ChatView({
message: string, message: string,
stream_url?: string stream_url?: string
) { ) {
// if (message === "") return;
chat.message = message; chat.message = message;
// chat is one of the chatHistory
updateFlowPool(chat.componentId, { updateFlowPool(chat.componentId, {
message, message,
sender_name: chat.sender_name ?? "Bot", sender_name: chat.sender_name ?? "Bot",
sender: chat.isSend ? "User" : "Machine", sender: chat.isSend ? "User" : "Machine",
}); });
// setChatHistory((oldChatHistory) => {
// const index = oldChatHistory.findIndex((ch) => ch.id === chat.id);
// if (index === -1) return oldChatHistory;
// let newChatHistory = _.cloneDeep(oldChatHistory);
// newChatHistory = [
// ...newChatHistory.slice(0, index),
// chat,
// ...newChatHistory.slice(index + 1),
// ];
// console.log("newChatHistory:", newChatHistory);
// return newChatHistory;
// });
} }
const [files, setFiles] = useState<FilePreviewType[]>([]); const [files, setFiles] = useState<FilePreviewType[]>([]);
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
@ -190,44 +180,6 @@ export default function ChatView({
aria-hidden="true" aria-hidden="true"
/> />
</Button> </Button>
{/* <Select
onValueChange={handleSelectChange}
value=""
disabled={lockChat}
>
<SelectTrigger className="">
<button className="flex gap-1">
<IconComponent
name="Eraser"
className={classNames(
"h-5 w-5 transition-all duration-100",
lockChat ? "animate-pulse text-primary" : "text-primary",
)}
aria-hidden="true"
/>
</button>
</SelectTrigger>
<SelectContent className="right-[9.5em]">
<SelectItem value="builds" className="cursor-pointer">
<div className="flex">
<IconComponent
name={"Trash2"}
className={`relative top-0.5 mr-2 h-4 w-4`}
/>
<span className="">Clear Builds</span>
</div>
</SelectItem>
<SelectItem value="buildsNSession" className="cursor-pointer">
<div className="flex">
<IconComponent
name={"Trash2"}
className={`relative top-0.5 mr-2 h-4 w-4`}
/>
<span className="">Clear Builds & Session</span>
</div>
</SelectItem>
</SelectContent>
</Select> */}
</div> </div>
<div ref={messagesRef} className="chat-message-div"> <div ref={messagesRef} className="chat-message-div">
{chatHistory?.length > 0 ? ( {chatHistory?.length > 0 ? (

View file

@ -10,7 +10,7 @@ import IconComponent from "../../components/genericIconComponent";
import { EXPORT_CODE_DIALOG } from "../../constants/constants"; import { EXPORT_CODE_DIALOG } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { useTweaksStore } from "../../stores/tweaksStore"; import { useTweaksStore } from "../../stores/tweaksStore";
import { InputFieldType } from "../../types/api"; import { TemplateVariableType } from "../../types/api";
import { uniqueTweakType } from "../../types/components"; import { uniqueTweakType } from "../../types/components";
import { FlowType } from "../../types/flow/index"; import { FlowType } from "../../types/flow/index";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
@ -39,7 +39,7 @@ const ApiModal = forwardRef(
open?: boolean; open?: boolean;
setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void; setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void;
}, },
ref ref,
) => { ) => {
const tweak = useTweaksStore((state) => state.tweak); const tweak = useTweaksStore((state) => state.tweak);
const addTweaks = useTweaksStore((state) => state.setTweak); const addTweaks = useTweaksStore((state) => state.setTweak);
@ -57,18 +57,18 @@ const ApiModal = forwardRef(
flow?.id, flow?.id,
autoLogin, autoLogin,
tweak, tweak,
flow?.endpoint_name flow?.endpoint_name,
); );
const curl_run_code = getCurlRunCode( const curl_run_code = getCurlRunCode(
flow?.id, flow?.id,
autoLogin, autoLogin,
tweak, tweak,
flow?.endpoint_name flow?.endpoint_name,
); );
const curl_webhook_code = getCurlWebhookCode( const curl_webhook_code = getCurlWebhookCode(
flow?.id, flow?.id,
autoLogin, autoLogin,
flow?.endpoint_name flow?.endpoint_name,
); );
const pythonCode = getPythonCode(flow?.name, tweak); const pythonCode = getPythonCode(flow?.name, tweak);
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin); const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
@ -83,7 +83,7 @@ const ApiModal = forwardRef(
pythonCode, pythonCode,
]; ];
const [tabs, setTabs] = useState( const [tabs, setTabs] = useState(
createTabsArray(codesArray, includeWebhook) createTabsArray(codesArray, includeWebhook),
); );
const canShowTweaks = const canShowTweaks =
@ -132,7 +132,7 @@ const ApiModal = forwardRef(
buildTweakObject( buildTweakObject(
nodeId, nodeId,
element.data.node.template[templateField].value, element.data.node.template[templateField].value,
element.data.node.template[templateField] element.data.node.template[templateField],
); );
} }
}); });
@ -149,7 +149,7 @@ const ApiModal = forwardRef(
async function buildTweakObject( async function buildTweakObject(
tw: string, tw: string,
changes: string | string[] | boolean | number | Object[] | Object, changes: string | string[] | boolean | number | Object[] | Object,
template: InputFieldType template: TemplateVariableType,
) { ) {
changes = getChangesType(changes, template); changes = getChangesType(changes, template);
@ -191,7 +191,7 @@ const ApiModal = forwardRef(
flow?.id, flow?.id,
autoLogin, autoLogin,
cloneTweak, cloneTweak,
flow?.endpoint_name flow?.endpoint_name,
); );
const pythonCode = getPythonCode(flow?.name, cloneTweak); const pythonCode = getPythonCode(flow?.name, cloneTweak);
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin); const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
@ -235,7 +235,7 @@ const ApiModal = forwardRef(
</BaseModal.Content> </BaseModal.Content>
</BaseModal> </BaseModal>
); );
} },
); );
export default ApiModal; export default ApiModal;

View file

@ -1,9 +1,9 @@
import { InputFieldType } from "../../../types/api"; import { TemplateVariableType } from "../../../types/api";
import { convertArrayToObj } from "../../../utils/reactflowUtils"; import { convertArrayToObj } from "../../../utils/reactflowUtils";
export const getChangesType = ( export const getChangesType = (
changes: string | string[] | boolean | number | Object[] | Object, changes: string | string[] | boolean | number | Object[] | Object,
template: InputFieldType template: TemplateVariableType,
) => { ) => {
if (typeof changes === "string" && template.type === "float") { if (typeof changes === "string" && template.type === "float") {
changes = parseFloat(changes); changes = parseFloat(changes);

View file

@ -11,10 +11,10 @@ export const getNodesWithDefaultValue = (flow) => {
.filter( .filter(
(templateField) => (templateField) =>
templateField.charAt(0) !== "_" && templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show && node.data.node.template[templateField]?.show &&
LANGFLOW_SUPPORTED_TYPES.has( LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField].type node.data.node.template[templateField].type,
) ),
) )
.map((n, i) => { .map((n, i) => {
arrNodesWithValues.push(node["id"]); arrNodesWithValues.push(node["id"]);

View file

@ -1,11 +1,11 @@
import { InputFieldType } from "../../../types/api"; import { TemplateVariableType } from "../../../types/api";
import { NodeType } from "../../../types/flow"; import { NodeType } from "../../../types/flow";
export const getValue = ( export const getValue = (
value: string, value: string,
node: NodeType, node: NodeType,
template: InputFieldType, template: TemplateVariableType,
tweak: Object[] tweak: Object[],
) => { ) => {
let returnValue = value ?? ""; let returnValue = value ?? "";

View file

@ -18,7 +18,7 @@ export default function FlowSettingsModal({
useEffect(() => { useEffect(() => {
setName(currentFlow!.name); setName(currentFlow!.name);
setDescription(currentFlow!.description); setDescription(currentFlow!.description);
}, [currentFlow!.name, currentFlow!.description, open]); }, [currentFlow?.name, currentFlow?.description, open]);
const [name, setName] = useState(currentFlow!.name); const [name, setName] = useState(currentFlow!.name);
const [description, setDescription] = useState(currentFlow!.description); const [description, setDescription] = useState(currentFlow!.description);
@ -40,6 +40,7 @@ export default function FlowSettingsModal({
list: [err?.response?.data.detail ?? ""], list: [err?.response?.data.detail ?? ""],
}); });
console.error(err); console.error(err);
setIsSaving(false);
}); });
} }

View file

@ -19,6 +19,7 @@ export default function LoginAdminPage() {
const [inputState, setInputState] = const [inputState, setInputState] =
useState<loginInputStateType>(CONTROL_LOGIN_STATE); useState<loginInputStateType>(CONTROL_LOGIN_STATE);
const { login, isAuthenticated, setUserData } = useContext(AuthContext); const { login, isAuthenticated, setUserData } = useContext(AuthContext);
const setLoading = useAlertStore((state) => state.setLoading);
const { password, username } = inputState; const { password, username } = inputState;
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
@ -35,6 +36,10 @@ export default function LoginAdminPage() {
}; };
onLogin(user) onLogin(user)
.then((user) => { .then((user) => {
console.log("admin page");
setLoading(true);
login(user.access_token); login(user.access_token);
navigate("/admin/"); navigate("/admin/");
}) })

View file

@ -38,6 +38,7 @@ import {
generateNodeFromFlow, generateNodeFromFlow,
getNodeId, getNodeId,
isValidConnection, isValidConnection,
reconnectEdges,
scapeJSONParse, scapeJSONParse,
updateIds, updateIds,
validateSelection, validateSelection,
@ -61,19 +62,19 @@ export default function Page({
const preventDefault = true; const preventDefault = true;
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const autoSaveCurrentFlow = useFlowsManagerStore( const autoSaveCurrentFlow = useFlowsManagerStore(
(state) => state.autoSaveCurrentFlow (state) => state.autoSaveCurrentFlow,
); );
const types = useTypesStore((state) => state.types); const types = useTypesStore((state) => state.types);
const templates = useTypesStore((state) => state.templates); const templates = useTypesStore((state) => state.templates);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [showCanvas, setSHowCanvas] = useState( const [showCanvas, setSHowCanvas] = useState(
Object.keys(templates).length > 0 && Object.keys(types).length > 0 Object.keys(templates).length > 0 && Object.keys(types).length > 0,
); );
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
const setReactFlowInstance = useFlowStore( const setReactFlowInstance = useFlowStore(
(state) => state.setReactFlowInstance (state) => state.setReactFlowInstance,
); );
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges); const edges = useFlowStore((state) => state.edges);
@ -90,10 +91,10 @@ export default function Page({
const paste = useFlowStore((state) => state.paste); const paste = useFlowStore((state) => state.paste);
const resetFlow = useFlowStore((state) => state.resetFlow); const resetFlow = useFlowStore((state) => state.resetFlow);
const lastCopiedSelection = useFlowStore( const lastCopiedSelection = useFlowStore(
(state) => state.lastCopiedSelection (state) => state.lastCopiedSelection,
); );
const setLastCopiedSelection = useFlowStore( const setLastCopiedSelection = useFlowStore(
(state) => state.setLastCopiedSelection (state) => state.setLastCopiedSelection,
); );
const onConnect = useFlowStore((state) => state.onConnect); const onConnect = useFlowStore((state) => state.onConnect);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
@ -116,7 +117,7 @@ export default function Page({
clonedSelection!, clonedSelection!,
clonedNodes, clonedNodes,
clonedEdges, clonedEdges,
getRandomName() getRandomName(),
); );
const newGroupNode = generateNodeFromFlow(newFlow, getNodeId); const newGroupNode = generateNodeFromFlow(newFlow, getNodeId);
// const newEdges = reconnectEdges(newGroupNode, removedEdges); // const newEdges = reconnectEdges(newGroupNode, removedEdges);
@ -124,8 +125,8 @@ export default function Page({
...clonedNodes.filter( ...clonedNodes.filter(
(oldNodes) => (oldNodes) =>
!clonedSelection?.nodes.some( !clonedSelection?.nodes.some(
(selectionNode) => selectionNode.id === oldNodes.id (selectionNode) => selectionNode.id === oldNodes.id,
) ),
), ),
newGroupNode, newGroupNode,
]); ]);
@ -212,7 +213,7 @@ export default function Page({
{ {
x: position.current.x, x: position.current.x,
y: position.current.y, y: position.current.y,
} },
); );
} }
} }
@ -296,7 +297,7 @@ export default function Page({
useEffect(() => { useEffect(() => {
setSHowCanvas( setSHowCanvas(
Object.keys(templates).length > 0 && Object.keys(types).length > 0 Object.keys(templates).length > 0 && Object.keys(types).length > 0,
); );
}, [templates, types]); }, [templates, types]);
@ -305,7 +306,7 @@ export default function Page({
takeSnapshot(); takeSnapshot();
onConnect(params); onConnect(params);
}, },
[takeSnapshot, onConnect] [takeSnapshot, onConnect],
); );
const onNodeDragStart: NodeDragHandler = useCallback(() => { const onNodeDragStart: NodeDragHandler = useCallback(() => {
@ -346,7 +347,7 @@ export default function Page({
// Extract the data from the drag event and parse it as a JSON object // Extract the data from the drag event and parse it as a JSON object
const data: { type: string; node?: APIClassType } = JSON.parse( const data: { type: string; node?: APIClassType } = JSON.parse(
event.dataTransfer.getData("nodedata") event.dataTransfer.getData("nodedata"),
); );
const newId = getNodeId(data.type); const newId = getNodeId(data.type);
@ -362,7 +363,7 @@ export default function Page({
}; };
paste( paste(
{ nodes: [newNode], edges: [] }, { nodes: [newNode], edges: [] },
{ x: event.clientX, y: event.clientY } { x: event.clientX, y: event.clientY },
); );
} else if (event.dataTransfer.types.some((types) => types === "Files")) { } else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot(); takeSnapshot();
@ -391,7 +392,7 @@ export default function Page({
} }
}, },
// Specify dependencies for useCallback // Specify dependencies for useCallback
[getNodeId, setNodes, takeSnapshot, paste] [getNodeId, setNodes, takeSnapshot, paste],
); );
const onEdgeUpdateStart = useCallback(() => { const onEdgeUpdateStart = useCallback(() => {
@ -407,7 +408,7 @@ export default function Page({
setEdges((els) => updateEdge(oldEdge, newConnection, els)); setEdges((els) => updateEdge(oldEdge, newConnection, els));
} }
}, },
[setEdges] [setEdges],
); );
const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => { const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => {
@ -440,7 +441,7 @@ export default function Page({
(flow: OnSelectionChangeParams): void => { (flow: OnSelectionChangeParams): void => {
setLastSelection(flow); setLastSelection(flow);
}, },
[] [],
); );
const onPaneClick = useCallback((flow) => { const onPaneClick = useCallback((flow) => {

View file

@ -57,17 +57,17 @@ export default function NodeToolbarComponent({
const nodeLength = Object.keys(data.node!.template).filter( const nodeLength = Object.keys(data.node!.template).filter(
(templateField) => (templateField) =>
templateField.charAt(0) !== "_" && templateField.charAt(0) !== "_" &&
data.node?.template[templateField].show && data.node?.template[templateField]?.show &&
(data.node.template[templateField].type === "str" || (data.node.template[templateField]?.type === "str" ||
data.node.template[templateField].type === "bool" || data.node.template[templateField]?.type === "bool" ||
data.node.template[templateField].type === "float" || data.node.template[templateField]?.type === "float" ||
data.node.template[templateField].type === "code" || data.node.template[templateField]?.type === "code" ||
data.node.template[templateField].type === "prompt" || data.node.template[templateField]?.type === "prompt" ||
data.node.template[templateField].type === "file" || data.node.template[templateField]?.type === "file" ||
data.node.template[templateField].type === "Any" || data.node.template[templateField]?.type === "Any" ||
data.node.template[templateField].type === "int" || data.node.template[templateField]?.type === "int" ||
data.node.template[templateField].type === "dict" || data.node.template[templateField]?.type === "dict" ||
data.node.template[templateField].type === "NestedDict") data.node.template[templateField]?.type === "NestedDict")
).length; ).length;
const hasStore = useStoreStore((state) => state.hasStore); const hasStore = useStoreStore((state) => state.hasStore);
@ -626,7 +626,7 @@ export default function NodeToolbarComponent({
/> />
</SelectItem> </SelectItem>
)} )}
{(!hasStore || !hasApiKey || !validApiKey) && ( {/* {(!hasStore || !hasApiKey || !validApiKey) && (
<SelectItem value={"Download"}> <SelectItem value={"Download"}>
<ToolbarSelectItem <ToolbarSelectItem
shortcut={ shortcut={
@ -638,7 +638,7 @@ export default function NodeToolbarComponent({
dataTestId="Download-button-modal" dataTestId="Download-button-modal"
/> />
</SelectItem> </SelectItem>
)} )} */}
<SelectItem <SelectItem
value={"documentation"} value={"documentation"}
disabled={data.node?.documentation === ""} disabled={data.node?.documentation === ""}
@ -688,16 +688,19 @@ export default function NodeToolbarComponent({
style={`${frozen ? " text-ice" : ""} transition-all`} style={`${frozen ? " text-ice" : ""} transition-all`}
/> />
</SelectItem> </SelectItem>
{(!hasStore || !hasApiKey || !validApiKey) && (
<SelectItem value="Download"> <SelectItem value="Download">
<ToolbarSelectItem <ToolbarSelectItem
shortcut={ shortcut={
shortcuts.find((obj) => obj.name === "Download")?.shortcut! shortcuts.find((obj) => obj.name === "Download")
?.shortcut!
} }
value={"Download"} value={"Download"}
icon={"Download"} icon={"Download"}
dataTestId="download-button-modal" dataTestId="download-button-modal"
/> />
</SelectItem> </SelectItem>
)}
<SelectItem <SelectItem
value={"delete"} value={"delete"}
className="focus:bg-red-400/[.20]" className="focus:bg-red-400/[.20]"

View file

@ -9,6 +9,7 @@ import { CONTROL_LOGIN_STATE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { onLogin } from "../../controllers/API"; import { onLogin } from "../../controllers/API";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { LoginType } from "../../types/api"; import { LoginType } from "../../types/api";
import { import {
inputHandlerEventType, inputHandlerEventType,
@ -20,10 +21,10 @@ export default function LoginPage(): JSX.Element {
useState<loginInputStateType>(CONTROL_LOGIN_STATE); useState<loginInputStateType>(CONTROL_LOGIN_STATE);
const { password, username } = inputState; const { password, username } = inputState;
const { login, isAuthenticated, setUserData, setIsAdmin } = const { login } = useContext(AuthContext);
useContext(AuthContext);
const navigate = useNavigate(); const navigate = useNavigate();
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setLoading = useFlowsManagerStore((state) => state.setIsLoading);
function handleInput({ function handleInput({
target: { name, value }, target: { name, value },
@ -38,6 +39,9 @@ export default function LoginPage(): JSX.Element {
}; };
onLogin(user) onLogin(user)
.then((user) => { .then((user) => {
console.log("login page");
setLoading(true);
login(user.access_token); login(user.access_token);
navigate("/"); navigate("/");
}) })

View file

@ -1,39 +1,19 @@
import { useState } from "react"; import { useState } from "react";
import { useLocation } from "react-router-dom";
import useAlertStore from "../../../../../../stores/alertStore";
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
import { useFolderStore } from "../../../../../../stores/foldersStore";
import { handleDownloadFolderFn } from "../../../../utils/handle-download-folder";
import InputSearchComponent from "../inputSearchComponent"; import InputSearchComponent from "../inputSearchComponent";
import TabsSearchComponent from "../tabsComponent"; import TabsSearchComponent from "../tabsComponent";
type HeaderTabsSearchComponentProps = {}; type HeaderTabsSearchComponentProps = {};
const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => { const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
const location = useLocation();
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const folderId = location?.state?.folderId || myCollectionId;
const isLoading = useFlowsManagerStore((state) => state.isLoading); const isLoading = useFlowsManagerStore((state) => state.isLoading);
const [tabActive, setTabActive] = useState("Flows"); const [tabActive, setTabActive] = useState("Flows");
const setErrorData = useAlertStore((state) => state.setErrorData);
const allFlows = useFlowsManagerStore((state) => state.allFlows);
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
const setSearchFlowsComponents = useFlowsManagerStore( const setSearchFlowsComponents = useFlowsManagerStore(
(state) => state.setSearchFlowsComponents (state) => state.setSearchFlowsComponents
); );
const handleDownloadFolder = () => {
if (allFlows.length === 0) {
setErrorData({
title: "Folder is empty",
list: [],
});
return;
}
handleDownloadFolderFn(folderId);
};
return ( return (
<> <>
<div className="relative flex items-end gap-4"> <div className="relative flex items-end gap-4">

View file

@ -2,11 +2,12 @@ import useAlertStore from "../../../stores/alertStore";
import { useFolderStore } from "../../../stores/foldersStore"; import { useFolderStore } from "../../../stores/foldersStore";
import { deleteFolder, getFolderById } from "../services"; import { deleteFolder, getFolderById } from "../services";
const useDeleteFolder = ({ navigate, getFoldersApi }) => { const useDeleteFolder = ({ navigate }) => {
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const folderToEdit = useFolderStore((state) => state.folderToEdit); const folderToEdit = useFolderStore((state) => state.folderToEdit);
const myCollectionId = useFolderStore((state) => state.myCollectionId); const myCollectionId = useFolderStore((state) => state.myCollectionId);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const handleDeleteFolder = () => { const handleDeleteFolder = () => {
deleteFolder(folderToEdit?.id!) deleteFolder(folderToEdit?.id!)

View file

@ -25,16 +25,9 @@ export default function HomePage(): JSX.Element {
const [openFolderModal, setOpenFolderModal] = useState(false); const [openFolderModal, setOpenFolderModal] = useState(false);
const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false); const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false);
const is_component = pathname === "/components"; const is_component = pathname === "/components";
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit); const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => {
setTimeout(() => {
getFoldersApi();
}, 300);
}, []);
useEffect(() => { useEffect(() => {
setCurrentFlowId(""); setCurrentFlowId("");
}, [pathname]); }, [pathname]);
@ -45,7 +38,7 @@ export default function HomePage(): JSX.Element {
is_component, is_component,
}); });
const { handleDeleteFolder } = useDeleteFolder({ navigate, getFoldersApi }); const { handleDeleteFolder } = useDeleteFolder({ navigate });
return ( return (
<> <>

View file

@ -45,9 +45,9 @@ const ProfilePictureFormComponent = ({
} else { } else {
prev[folder] = [path]; prev[folder] = [path];
} }
setLoading(false);
return prev; return prev;
}); });
setLoading(false);
}); });
} }
}) })

View file

@ -27,7 +27,7 @@ export default function MessagesPage() {
setSelectedRows, setSelectedRows,
setSuccessData, setSuccessData,
setErrorData, setErrorData,
selectedRows, selectedRows
); );
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData); const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
@ -61,7 +61,7 @@ export default function MessagesPage() {
overlayNoRowsTemplate="No data available" overlayNoRowsTemplate="No data available"
onSelectionChanged={(event: SelectionChangedEvent) => { onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows( setSelectedRows(
event.api.getSelectedRows().map((row) => row.index), event.api.getSelectedRows().map((row) => row.index)
); );
}} }}
rowSelection="multiple" rowSelection="multiple"

View file

@ -7,20 +7,21 @@ import {
} from "../pages/MainPage/services"; } from "../pages/MainPage/services";
import { FoldersStoreType } from "../types/zustand/folders"; import { FoldersStoreType } from "../types/zustand/folders";
import useFlowsManagerStore from "./flowsManagerStore"; import useFlowsManagerStore from "./flowsManagerStore";
import { useTypesStore } from "./typesStore";
export const useFolderStore = create<FoldersStoreType>((set, get) => ({ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
folders: [], folders: [],
getFoldersApi: (refetch = false) => { getFoldersApi: (refetch = false, startupApplication: boolean = false) => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
if (get()?.folders.length === 0 || refetch === true) { if (get()?.folders.length === 0 || refetch === true) {
get().setLoading(true); get().setIsLoadingFolders(true);
getFolders().then( getFolders().then(
(res) => { async (res) => {
const foldersWithoutStarterProjects = res.filter( const foldersWithoutStarterProjects = res?.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME (folder) => folder.name !== STARTER_FOLDER_NAME
); );
const starterProjects = res.find( const starterProjects = res?.find(
(folder) => folder.name === STARTER_FOLDER_NAME (folder) => folder.name === STARTER_FOLDER_NAME
); );
@ -33,45 +34,77 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
set({ myCollectionId }); set({ myCollectionId });
if (refetch === true) { const { refreshFlows } = useFlowsManagerStore.getState();
useFlowsManagerStore.getState().refreshFlows(); const { getTypes } = useTypesStore.getState();
useFlowsManagerStore.getState().setAllFlows; const { setIsLoadingFolders } = get();
}
if (refetch) {
if (startupApplication) {
await refreshFlows();
await getTypes();
} else {
refreshFlows();
getTypes();
}
}
setIsLoadingFolders(false);
get().setLoading(false);
resolve(); resolve();
}, },
(error) => { (error) => {
set({ folders: [] }); set({ folders: [] });
get().setLoading(false); get().setIsLoadingFolders(false);
reject(error); reject(error);
} }
); );
} }
}); });
}, },
refreshFolders: () => {
return new Promise<void>((resolve, reject) => {
getFolders().then(
async (res) => {
const foldersWithoutStarterProjects = res?.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME
);
const starterProjects = res?.find(
(folder) => folder.name === STARTER_FOLDER_NAME
);
set({ starterProjectId: starterProjects!.id ?? "" });
set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find(
(f) => f.name === DEFAULT_FOLDER
)?.id;
set({ myCollectionId });
resolve();
},
(error) => {
set({ folders: [] });
get().setIsLoadingFolders(false);
reject(error);
}
);
});
},
setFolders: (folders) => set(() => ({ folders: folders })), setFolders: (folders) => set(() => ({ folders: folders })),
loading: false, isLoadingFolders: false,
setLoading: (loading) => set(() => ({ loading: loading })), setIsLoadingFolders: (isLoadingFolders) => set(() => ({ isLoadingFolders })),
getFolderById: (id) => { getFolderById: (id) => {
get().setLoadingById(true);
if (id) { if (id) {
getFolderById(id).then( getFolderById(id).then((res) => {
(res) => {
const setAllFlows = useFlowsManagerStore.getState().setAllFlows; const setAllFlows = useFlowsManagerStore.getState().setAllFlows;
setAllFlows(res.flows); setAllFlows(res.flows);
set({ selectedFolder: res }); set({ selectedFolder: res });
get().setLoadingById(false); });
},
() => {
get().setLoadingById(false);
}
);
} }
}, },
selectedFolder: null, selectedFolder: null,
loadingById: false, loadingById: false,
setLoadingById: (loading) => set(() => ({ loadingById: loading })),
getMyCollectionFolder: () => { getMyCollectionFolder: () => {
const folders = get().folders; const folders = get().folders;
const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id; const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id;

View file

@ -177,6 +177,7 @@ export type VertexBuildTypeAPI = {
timestamp: string; timestamp: string;
params: any; params: any;
messages: ChatOutputType[] | ChatInputType[]; messages: ChatOutputType[] | ChatInputType[];
artifacts: any | ChatOutputType | ChatInputType;
}; };
export type LogType = { export type LogType = {

View file

@ -75,7 +75,7 @@ export type ParameterComponentType = {
info?: string; info?: string;
proxy?: { field: string; id: string }; proxy?: { field: string; id: string };
showNode?: boolean; showNode?: boolean;
index: number; index?: string;
onCloseModal?: (close: boolean) => void; onCloseModal?: (close: boolean) => void;
outputName?: string; outputName?: string;
outputProxy?: OutputFieldProxyType; outputProxy?: OutputFieldProxyType;
@ -511,7 +511,7 @@ export type ChatInputType = {
isDragging: boolean; isDragging: boolean;
files: FilePreviewType[]; files: FilePreviewType[];
setFiles: ( setFiles: (
files: FilePreviewType[] | ((prev: FilePreviewType[]) => FilePreviewType[]) files: FilePreviewType[] | ((prev: FilePreviewType[]) => FilePreviewType[]),
) => void; ) => void;
chatValue: string; chatValue: string;
inputRef: { inputRef: {
@ -614,7 +614,7 @@ export type chatMessagePropsType = {
updateChat: ( updateChat: (
chat: ChatMessageType, chat: ChatMessageType,
message: string, message: string,
stream_url?: string stream_url?: string,
) => void; ) => void;
}; };

View file

@ -2,14 +2,15 @@ import { FolderType } from "../../../pages/MainPage/entities";
export type FoldersStoreType = { export type FoldersStoreType = {
folders: FolderType[]; folders: FolderType[];
getFoldersApi: (refetch?: boolean) => Promise<void>; getFoldersApi: (
refetch?: boolean,
startupApplication?: boolean
) => Promise<void>;
setFolders: (folders: FolderType[]) => void; setFolders: (folders: FolderType[]) => void;
loading: boolean; isLoadingFolders: boolean;
setLoading: (loading: boolean) => void; setIsLoadingFolders: (isLoadingFolders: boolean) => void;
selectedFolder: FolderType | null; selectedFolder: FolderType | null;
getFolderById: (id: string) => void; getFolderById: (id: string) => void;
loadingById: boolean;
setLoadingById: (loading: boolean) => void;
getMyCollectionFolder: () => void; getMyCollectionFolder: () => void;
myCollectionFlows: FolderType | null; myCollectionFlows: FolderType | null;
myCollectionId: string | null; myCollectionId: string | null;
@ -25,4 +26,5 @@ export type FoldersStoreType = {
setFolderIdDragging: (id: string) => void; setFolderIdDragging: (id: string) => void;
starterProjectId: string; starterProjectId: string;
setStarterProjectId: (id: string) => void; setStarterProjectId: (id: string) => void;
refreshFolders: () => void;
}; };

View file

@ -17,7 +17,7 @@ type BuildVerticesParams = {
onBuildUpdate?: ( onBuildUpdate?: (
data: VertexBuildTypeAPI, data: VertexBuildTypeAPI,
status: BuildStatus, status: BuildStatus,
buildId: string buildId: string,
) => void; // Replace any with the actual type if it's not any ) => void; // Replace any with the actual type if it's not any
onBuildComplete?: (allNodesValid: boolean) => void; onBuildComplete?: (allNodesValid: boolean) => void;
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void; onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
@ -55,7 +55,7 @@ export async function updateVerticesOrder(
startNodeId?: string | null, startNodeId?: string | null,
stopNodeId?: string | null, stopNodeId?: string | null,
nodes?: Node[], nodes?: Node[],
edges?: Edge[] edges?: Edge[],
): Promise<{ ): Promise<{
verticesLayers: VertexLayerElementType[][]; verticesLayers: VertexLayerElementType[][];
verticesIds: string[]; verticesIds: string[];
@ -71,7 +71,7 @@ export async function updateVerticesOrder(
startNodeId, startNodeId,
stopNodeId, stopNodeId,
nodes, nodes,
edges edges,
); );
} catch (error: any) { } catch (error: any) {
setErrorData({ setErrorData({
@ -128,7 +128,7 @@ export async function buildVertices({
startNodeId, startNodeId,
stopNodeId, stopNodeId,
nodes, nodes,
edges edges,
); );
if (onValidateNodes) { if (onValidateNodes) {
try { try {
@ -162,7 +162,6 @@ export async function buildVertices({
const currentLayer = const currentLayer =
useFlowStore.getState().verticesBuild?.verticesLayers![currentLayerIndex]; useFlowStore.getState().verticesBuild?.verticesLayers![currentLayerIndex];
// If there are no more layers, we are done // If there are no more layers, we are done
console.log("currentLayer", currentLayer);
if (!currentLayer) { if (!currentLayer) {
if (onBuildComplete) { if (onBuildComplete) {
const allNodesValid = buildResults.every((result) => result); const allNodesValid = buildResults.every((result) => result);
@ -191,14 +190,14 @@ export async function buildVertices({
onBuildUpdate( onBuildUpdate(
getInactiveVertexData(element.id), getInactiveVertexData(element.id),
BuildStatus.INACTIVE, BuildStatus.INACTIVE,
runId runId,
); );
} }
if (element.reference) { if (element.reference) {
onBuildUpdate( onBuildUpdate(
getInactiveVertexData(element.reference), getInactiveVertexData(element.reference),
BuildStatus.INACTIVE, BuildStatus.INACTIVE,
runId runId,
); );
} }
buildResults.push(false); buildResults.push(false);
@ -224,7 +223,7 @@ export async function buildVertices({
if (stop) { if (stop) {
return; return;
} }
}) }),
); );
// Once the current layer is built, move to the next layer // Once the current layer is built, move to the next layer
currentLayerIndex += 1; currentLayerIndex += 1;
@ -289,7 +288,10 @@ async function buildVertex({
console.error(error); console.error(error);
onBuildError!( onBuildError!(
"Error Building Component", "Error Building Component",
[(error as AxiosError<any>).response?.data?.detail ?? "Unknown Error"], [
(error as AxiosError<any>).response?.data?.detail ??
"An unexpected error occurred while building the Component. Please try again.",
],
verticesIds.map((id) => ({ id })) verticesIds.map((id) => ({ id }))
); );
stopBuild(); stopBuild();

View file

@ -56,7 +56,7 @@ export function normalCaseToSnakeCase(str: string): string {
export function toTitleCase( export function toTitleCase(
str: string | undefined, str: string | undefined,
isNodeField?: boolean, isNodeField?: boolean
): string { ): string {
if (!str) return ""; if (!str) return "";
let result = str let result = str
@ -65,7 +65,7 @@ export function toTitleCase(
if (isNodeField) return word; if (isNodeField) return word;
if (index === 0) { if (index === 0) {
return checkUpperWords( return checkUpperWords(
word[0].toUpperCase() + word.slice(1).toLowerCase(), word[0].toUpperCase() + word.slice(1).toLowerCase()
); );
} }
return checkUpperWords(word.toLowerCase()); return checkUpperWords(word.toLowerCase());
@ -78,7 +78,7 @@ export function toTitleCase(
if (isNodeField) return word; if (isNodeField) return word;
if (index === 0) { if (index === 0) {
return checkUpperWords( return checkUpperWords(
word[0].toUpperCase() + word.slice(1).toLowerCase(), word[0].toUpperCase() + word.slice(1).toLowerCase()
); );
} }
return checkUpperWords(word.toLowerCase()); return checkUpperWords(word.toLowerCase());
@ -182,7 +182,7 @@ export function checkLocalStorageKey(key: string): boolean {
export function IncrementObjectKey( export function IncrementObjectKey(
object: object, object: object,
key: string, key: string
): { newKey: string; increment: number } { ): { newKey: string; increment: number } {
let count = 1; let count = 1;
const type = removeCountFromString(key); const type = removeCountFromString(key);
@ -217,7 +217,7 @@ export function groupByFamily(
data: APIDataType, data: APIDataType,
baseClasses: string, baseClasses: string,
left: boolean, left: boolean,
flow?: NodeType[], flow?: NodeType[]
): groupedObjType[] { ): groupedObjType[] {
const baseClassesSet = new Set(baseClasses.split("\n")); const baseClassesSet = new Set(baseClasses.split("\n"));
let arrOfPossibleInputs: Array<{ let arrOfPossibleInputs: Array<{
@ -237,13 +237,13 @@ export function groupByFamily(
const checkBaseClass = (template: InputFieldType) => { const checkBaseClass = (template: InputFieldType) => {
return ( return (
template.type && template?.type &&
template.show && template?.show &&
((!excludeTypes.has(template.type) && ((!excludeTypes.has(template.type) &&
baseClassesSet.has(template.type)) || baseClassesSet.has(template.type)) ||
(template.input_types && (template?.input_types &&
template.input_types.some((inputType) => template?.input_types.some((inputType) =>
baseClassesSet.has(inputType), baseClassesSet.has(inputType)
))) )))
); );
}; };
@ -263,7 +263,7 @@ export function groupByFamily(
hasBaseClassInBaseClasses: hasBaseClassInBaseClasses:
foundNode?.hasBaseClassInBaseClasses || foundNode?.hasBaseClassInBaseClasses ||
nodeData.node!.base_classes?.some((baseClass) => nodeData.node!.base_classes?.some((baseClass) =>
baseClassesSet.has(baseClass), baseClassesSet.has(baseClass)
), //seta como anterior ou verifica se o node tem base class ), //seta como anterior ou verifica se o node tem base class
displayName: nodeData.node?.display_name, displayName: nodeData.node?.display_name,
}); });
@ -280,10 +280,10 @@ export function groupByFamily(
if (!foundNode) { if (!foundNode) {
foundNode = { foundNode = {
hasBaseClassInTemplate: Object.values(node!.template).some( hasBaseClassInTemplate: Object.values(node!.template).some(
checkBaseClass, checkBaseClass
), ),
hasBaseClassInBaseClasses: node!.base_classes?.some((baseClass) => hasBaseClassInBaseClasses: node!.base_classes?.some((baseClass) =>
baseClassesSet.has(baseClass), baseClassesSet.has(baseClass)
), ),
displayName: node?.display_name, displayName: node?.display_name,
}; };
@ -355,7 +355,7 @@ export function isTimeStampString(str: string): boolean {
export function extractColumnsFromRows( export function extractColumnsFromRows(
rows: object[], rows: object[],
mode: "intersection" | "union", mode: "intersection" | "union",
excludeColumns?: Array<string>, excludeColumns?: Array<string>
): (ColDef<any> | ColGroupDef<any>)[] { ): (ColDef<any> | ColGroupDef<any>)[] {
let columnsKeys: { [key: string]: ColDef<any> | ColGroupDef<any> } = {}; let columnsKeys: { [key: string]: ColDef<any> | ColGroupDef<any> } = {};
if (rows.length === 0) { if (rows.length === 0) {

View file

@ -40,6 +40,7 @@ test("CodeAreaModalComponent", async ({ page }) => {
await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click();
await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click();
await page.getByTestId("div-generic-node").click(); await page.getByTestId("div-generic-node").click();
await page.getByTestId("code-button-modal").click(); await page.getByTestId("code-button-modal").click();
const wCode = const wCode =

View file

@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
import pytest import pytest
from langflow.services.auth.utils import create_super_user, get_password_hash from langflow.services.auth.utils import create_super_user, get_password_hash
from langflow.services.database.models.user import UserUpdate from langflow.services.database.models.user import UserUpdate
from langflow.services.database.models.user.model import User from langflow.services.database.models.user.model import User
@ -95,7 +96,7 @@ def test_data_consistency_after_update(client, active_user, logged_in_headers, s
# Fetch the updated user from the database # Fetch the updated user from the database
response = client.get("/api/v1/users/whoami", headers=logged_in_headers) response = client.get("/api/v1/users/whoami", headers=logged_in_headers)
assert response.status_code == 401, response.json() assert response.status_code == 401, response.json()
assert response.json()["detail"] == "Could not validate credentials" assert response.json()["detail"] == "User not found or is inactive."
def test_data_consistency_after_delete(client, test_user, super_user_headers): def test_data_consistency_after_delete(client, test_user, super_user_headers):