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"
jobs:
docker_build:
name: Build Docker Image
setup:
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:
- 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: Login to Docker Hub
@ -38,56 +47,54 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set Dockerfile and Tags
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
- name: Build and push Base Image
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: "linux/amd64,linux/arm64/v8"
file: ${{ env.DOCKERFILE }}
tags: ${{ env.TAGS }}
- name: Wait for Docker Hub to propagate
run: sleep 120
- name: Build and push (backend)
if: ${{ inputs.release_type == 'main' }}
file: ./docker/build_and_push_base.Dockerfile
tags: ${{ needs.setup.outputs.base_tags }}
build_components:
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
with:
context: .
push: true
platforms: "linux/amd64,linux/arm64/v8"
file: ./docker/build_and_push_backend.Dockerfile
build-args: |
LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }}
tags: |
langflowai/langflow-backend:${{ inputs.version }}
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
file: ${{ matrix.dockerfile }}
tags: ${{ matrix.tags }}
restart-space:
name: Restart HuggingFace Spaces
if: ${{ inputs.release_type == 'main' }}
runs-on: ubuntu-latest
needs: docker_build
needs: build_base
strategy:
matrix:
python-version:

View file

@ -24,6 +24,7 @@ env:
jobs:
test-docker:
runs-on: ubuntu-latest
name: Test docker images
steps:
- uses: actions/checkout@v4
- name: Build image
@ -61,20 +62,3 @@ jobs:
docker build -t langflowai/langflow-frontend:latest-dev \
-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:
push:
branches: [main]
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
pull_request:
paths:
- "poetry.lock"
- "pyproject.toml"
- "src/backend/**"
- "tests/**"
env:
POETRY_VERSION: "1.8.2"
@ -45,6 +40,13 @@ jobs:
path: |
./.mypy_cache
key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }}
- name: Lint check
run: |
make lint
- name: Run linters
uses: wearerequired/lint-action@v1
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
db.sqlite3
db.sqlite3-journal
*.db-shm
*.db-wal
# Flask stuff:
instance/

View file

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

View file

@ -7,8 +7,9 @@
# Used to build deps + create our virtual environment
################################
# force platform to the current architecture to increase build speed time on multi-platform builds
FROM --platform=$BUILDPLATFORM python:3.12-slim as builder-base
# 1. use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved
# 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 \
\
@ -51,15 +52,27 @@ COPY pyproject.toml poetry.lock README.md ./
COPY src/ ./src
COPY scripts/ ./scripts
RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py
# 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 \
# 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 run pip install dist/*.whl --force-reinstall
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl
################################
# RUNTIME
# 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 \
&& apt-get install --no-install-recommends -y \

View file

@ -10,7 +10,9 @@
# PYTHON-BASE
# 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
ENV PYTHONUNBUFFERED=1 \

View file

@ -5,7 +5,7 @@
# 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
COPY src/frontend /frontend

View file

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

View file

@ -4,11 +4,20 @@ trap 'kill -TERM $PID' TERM INT
if [ -z "$BACKEND_URL" ]; then
BACKEND_URL="$1"
fi
if [ -z "$FRONTEND_PORT" ]; then
FRONTEND_PORT="$2"
fi
if [ -z "$FRONTEND_PORT" ]; then
FRONTEND_PORT="80"
fi
if [ -z "$BACKEND_URL" ]; then
echo "BACKEND_URL must be set as an environment variable or as first parameter. (e.g. http://localhost:7860)"
exit 1
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|__FRONTEND_PORT__|$FRONTEND_PORT|g" /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]]
name = "boto3"
version = "1.34.125"
version = "1.34.126"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "boto3-1.34.125-py3-none-any.whl", hash = "sha256:116d9eb3c26cf313a2e1e44ef704d1f98f9eb18e7628695d07b01b44a8683544"},
{file = "boto3-1.34.125.tar.gz", hash = "sha256:31c4a5e4d6f9e6116be61ff654b424ddbd1afcdefe0e8b870c4796f9108eb1c6"},
{file = "boto3-1.34.126-py3-none-any.whl", hash = "sha256:7f676daef674fe74f34ce4063228eccc6e60c811f574720e31f230296c4bf29a"},
{file = "boto3-1.34.126.tar.gz", hash = "sha256:7e8418b47dd43954a9088d504541bed8a42b6d06e712d02befba134c1c4d7c6d"},
]
[package.dependencies]
botocore = ">=1.34.125,<1.35.0"
botocore = ">=1.34.126,<1.35.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@ -490,13 +490,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
version = "1.34.125"
version = "1.34.126"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
{file = "botocore-1.34.125-py3-none-any.whl", hash = "sha256:71e97e7d2c088f1188ba6976441b5857a5425acd4aaa31b45d13119c9cb86424"},
{file = "botocore-1.34.125.tar.gz", hash = "sha256:d2882be011ad5b16e7ab4a96360b5b66a0a7e175c1ea06dbf2de473c0a0a33d8"},
{file = "botocore-1.34.126-py3-none-any.whl", hash = "sha256:7eff883c638fe30e0b036789df32d851e093d12544615a3b90062b42ac85bdbc"},
{file = "botocore-1.34.126.tar.gz", hash = "sha256:7a8ccb6a7c02456757a984a3a44331b6f51c94cb8b9b287cd045122fd177a4b0"},
]
[package.dependencies]
@ -4113,22 +4113,25 @@ adal = ["adal (>=1.0.2)"]
[[package]]
name = "langchain"
version = "0.2.3"
version = "0.2.4"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langchain-0.2.3-py3-none-any.whl", hash = "sha256:5dc33cd9c8008693d328b7cb698df69073acecc89ad9c2a95f243b3314f8d834"},
{file = "langchain-0.2.3.tar.gz", hash = "sha256:81962cc72cce6515f7bd71e01542727870789bf8b666c6913d85559080c1a201"},
{file = "langchain-0.2.4-py3-none-any.whl", hash = "sha256:a04813215c30f944df006031e2febde872af8fab628dcee825d969e07b6cd621"},
{file = "langchain-0.2.4.tar.gz", hash = "sha256:e704b5b06222d5eba2d02c76f891321d1bac8952ed54e093831b2bdabf99dcd5"},
]
[package.dependencies]
aiohttp = ">=3.8.3,<4.0.0"
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"
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"
PyYAML = ">=5.3"
requests = ">=2,<3"
@ -4224,19 +4227,19 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]]
name = "langchain-core"
version = "0.2.5"
version = "0.2.6"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langchain_core-0.2.5-py3-none-any.whl", hash = "sha256:abe5138f22acff23a079ec538be5268bbf97cf023d51987a0dd474d2a16cae3e"},
{file = "langchain_core-0.2.5.tar.gz", hash = "sha256:4a5c2f56b22396a63ef4790043660e393adbfa6832b978f023ca996a04b8e752"},
{file = "langchain_core-0.2.6-py3-none-any.whl", hash = "sha256:90521c9fc95d8f925e0d2e2d952382676aea6d3f8de611eda1b1810874c31e5d"},
{file = "langchain_core-0.2.6.tar.gz", hash = "sha256:9f0e38da722a558a6e95b6d86de01bd92e84558c47ac8ba599f02eab70a1c873"},
]
[package.dependencies]
jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.1.66,<0.2.0"
packaging = ">=23.2,<24.0"
langsmith = ">=0.1.75,<0.2.0"
packaging = ">=23.2,<25"
pydantic = ">=1,<3"
PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0"
@ -4409,7 +4412,7 @@ six = "*"
[[package]]
name = "langflow-base"
version = "0.0.63"
version = "0.0.68"
description = "A Python package with a built-in web application"
optional = false
python-versions = ">=3.10,<3.13"
@ -4506,13 +4509,13 @@ requests = ">=2,<3"
[[package]]
name = "litellm"
version = "1.40.9"
version = "1.40.12"
description = "Library to easily interface with LLM API providers"
optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [
{file = "litellm-1.40.9-py3-none-any.whl", hash = "sha256:8a4107b9cce114d822de52cbc9bce56f8edc6620f19d0f2257e71834715fb366"},
{file = "litellm-1.40.9.tar.gz", hash = "sha256:e0ea07d0b55001a6f60bba2b2ecd72d1f0dca07e656f63937adfdf45f31e5ad7"},
{file = "litellm-1.40.12-py3-none-any.whl", hash = "sha256:42f1648507f29c60543ba5fdf35d38fc161694da043b201508225bae50d3328c"},
{file = "litellm-1.40.12.tar.gz", hash = "sha256:366bb9c3694b9ef59b3d073bb37ff9ca175ab4090dc187b0a11d2b21db3a6a5d"},
]
[package.dependencies]
@ -7850,13 +7853,13 @@ websockets = ">=11,<13"
[[package]]
name = "redis"
version = "5.0.5"
version = "5.0.6"
description = "Python client for Redis database and key-value store"
optional = true
python-versions = ">=3.7"
files = [
{file = "redis-5.0.5-py3-none-any.whl", hash = "sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada"},
{file = "redis-5.0.5.tar.gz", hash = "sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae"},
{file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"},
{file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"},
]
[package.dependencies]

View file

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

View file

@ -1,3 +1,4 @@
from typing import Optional
from langchain_core.embeddings import Embeddings
from langchain_openai import AzureOpenAIEmbeddings
from pydantic.v1 import SecretStr
@ -44,6 +45,11 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
"password": True,
},
"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(
@ -52,6 +58,7 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
azure_deployment: str,
api_version: str,
api_key: str,
dimensions: Optional[int] = None,
) -> Embeddings:
if api_key:
azure_api_key = SecretStr(api_key)
@ -63,6 +70,7 @@ class AzureOpenAIEmbeddingsComponent(CustomComponent):
azure_deployment=azure_deployment,
api_version=api_version,
api_key=azure_api_key,
dimensions=dimensions,
)
except Exception as e:

View file

@ -84,6 +84,11 @@ class OpenAIEmbeddingsComponent(CustomComponent):
"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(
@ -109,6 +114,7 @@ class OpenAIEmbeddingsComponent(CustomComponent):
skip_empty: bool = False,
tiktoken_enable: bool = True,
tiktoken_model_name: Optional[str] = None,
dimensions: Optional[int] = None,
) -> Embeddings:
# This is to avoid errors with Vector Stores (e.g Chroma)
if disallowed_special == ["all"]:
@ -140,4 +146,5 @@ class OpenAIEmbeddingsComponent(CustomComponent):
show_progress_bar=show_progress_bar,
skip_empty=skip_empty,
tiktoken_model_name=tiktoken_model_name,
dimensions=dimensions,
)

View file

@ -53,7 +53,7 @@ class ChatOpenAIComponent(CustomComponent):
self,
max_tokens: Optional[int] = 0,
model_kwargs: NestedDict = {},
model_name: str = "gpt-4o",
model_name: str = "gpt-3.5-turbo",
openai_api_base: Optional[str] = None,
openai_api_key: Optional[str] = None,
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.",
},
"token": {
"display_name": "Token",
"display_name": "Astra DB Application Token",
"info": "Authentication token for accessing Astra DB.",
"password": True,
},

View file

@ -3,7 +3,6 @@ from typing import List, Optional
import chromadb
from chromadb.config import Settings
from langchain_chroma import Chroma
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.field_typing import Embeddings, Text
from langflow.schema import Data
@ -104,10 +103,11 @@ class ChromaSearchComponent(LCVectorStoreComponent):
client = chromadb.HttpClient(settings=chroma_settings)
if index_directory:
index_directory = self.resolve_path(index_directory)
vector_store = Chroma(
embedding_function=embedding,
collection_name=collection_name,
persist_directory=index_directory,
persist_directory=index_directory or None,
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.",
},
"token": {
"display_name": "Token",
"display_name": "Astra DB Application Token",
"info": "Authentication token for accessing Astra DB.",
"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 pydantic import BaseModel
from langflow.interface.utils import extract_input_variables_from_prompt
from langflow.schema.message import Message
class UnbuiltObject:
@ -14,6 +16,16 @@ class UnbuiltResult:
pass
class ArtifactType(str, Enum):
TEXT = "text"
RECORD = "record"
OBJECT = "object"
ARRAY = "array"
STREAM = "stream"
UNKNOWN = "unknown"
MESSAGE = "message"
def validate_prompt(prompt: str):
"""Validate prompt."""
if extract_input_variables_from_prompt(prompt):
@ -50,3 +62,37 @@ def serialize_field(value):
elif isinstance(value, str):
return {"result": 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 = []
if source_node is None:
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]:
deduplicated_outputs.append(output)

View file

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

View file

@ -13,12 +13,7 @@
"targetHandle": {
"fieldName": "reference_2",
"id": "Prompt-Rse03",
"inputTypes": [
"Document",
"BaseOutputParser",
"Record",
"Text"
],
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
"type": "str"
}
},
@ -39,17 +34,12 @@
"dataType": "OpenAIModel",
"id": "OpenAIModel-gi29P",
"name": "text_output",
"output_types": [
"Text"
]
"output_types": ["Text"]
},
"targetHandle": {
"fieldName": "input_value",
"id": "ChatOutput-JPlxl",
"inputTypes": [
"Text",
"Message"
],
"inputTypes": ["Text", "Message"],
"type": "str"
}
},
@ -74,12 +64,7 @@
"targetHandle": {
"fieldName": "reference_1",
"id": "Prompt-Rse03",
"inputTypes": [
"Document",
"BaseOutputParser",
"Record",
"Text"
],
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
"type": "str"
}
},
@ -99,19 +84,12 @@
"dataType": "TextInput",
"id": "TextInput-og8Or",
"name": "Text",
"output_types": [
"Text"
]
"output_types": ["Text"]
},
"targetHandle": {
"fieldName": "instructions",
"id": "Prompt-Rse03",
"inputTypes": [
"Document",
"BaseOutputParser",
"Record",
"Text"
],
"inputTypes": ["Document", "BaseOutputParser", "Record", "Text"],
"type": "str"
}
},
@ -131,18 +109,12 @@
"dataType": "Prompt",
"id": "Prompt-Rse03",
"name": "prompt",
"output_types": [
"Prompt"
]
"output_types": ["Prompt"]
},
"targetHandle": {
"fieldName": "input_value",
"id": "OpenAIModel-gi29P",
"inputTypes": [
"Text",
"Data",
"Prompt"
],
"inputTypes": ["Text", "Data", "Prompt"],
"type": "str"
}
},
@ -164,18 +136,10 @@
"display_name": "Prompt",
"id": "Prompt-Rse03",
"node": {
"base_classes": [
"object",
"Text",
"str"
],
"base_classes": ["object", "Text", "str"],
"beta": false,
"custom_fields": {
"template": [
"reference_1",
"reference_2",
"instructions"
]
"template": ["reference_1", "reference_2", "instructions"]
},
"description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt",
@ -198,9 +162,7 @@
"method": "build_prompt",
"name": "prompt",
"selected": "Prompt",
"types": [
"Prompt"
],
"types": ["Prompt"],
"value": "__UNDEFINED__"
},
{
@ -209,9 +171,7 @@
"method": "format_prompt",
"name": "text",
"selected": "Text",
"types": [
"Text"
],
"types": ["Text"],
"value": "__UNDEFINED__"
}
],
@ -320,9 +280,7 @@
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -358,9 +316,7 @@
"data": {
"id": "URL-HYPkR",
"node": {
"base_classes": [
"Record"
],
"base_classes": ["Record"],
"beta": false,
"custom_fields": {
"urls": null
@ -380,9 +336,7 @@
"method": "fetch_content",
"name": "data",
"selected": "Data",
"types": [
"Data"
],
"types": ["Data"],
"value": "__UNDEFINED__"
}
],
@ -404,7 +358,7 @@
"show": true,
"title_case": false,
"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": {
"advanced": false,
@ -444,12 +398,7 @@
"data": {
"id": "ChatOutput-JPlxl",
"node": {
"base_classes": [
"Text",
"Record",
"object",
"str"
],
"base_classes": ["Text", "Record", "object", "str"],
"beta": false,
"custom_fields": {
"input_value": null,
@ -474,9 +423,7 @@
"method": "message_response",
"name": "message",
"selected": "Message",
"types": [
"Message"
],
"types": ["Message"],
"value": "__UNDEFINED__"
}
],
@ -507,10 +454,7 @@
"fileTypes": [],
"file_path": "",
"info": "Message to be passed as output.",
"input_types": [
"Text",
"Message"
],
"input_types": ["Text", "Message"],
"list": false,
"load_from_db": false,
"multiline": true,
@ -530,17 +474,12 @@
"fileTypes": [],
"file_path": "",
"info": "Type of sender.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": true,
"load_from_db": false,
"multiline": false,
"name": "sender",
"options": [
"Machine",
"User"
],
"options": ["Machine", "User"],
"password": false,
"placeholder": "",
"required": false,
@ -556,9 +495,7 @@
"fileTypes": [],
"file_path": "",
"info": "Name of the sender.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -578,9 +515,7 @@
"fileTypes": [],
"file_path": "",
"info": "Session ID for the message.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -611,11 +546,7 @@
"data": {
"id": "OpenAIModel-gi29P",
"node": {
"base_classes": [
"str",
"Text",
"object"
],
"base_classes": ["str", "Text", "object"],
"beta": false,
"custom_fields": {
"input_value": null,
@ -653,9 +584,7 @@
"method": "text_response",
"name": "text_output",
"selected": "Text",
"types": [
"Text"
],
"types": ["Text"],
"value": "__UNDEFINED__"
},
{
@ -664,9 +593,7 @@
"method": "build_model",
"name": "model_output",
"selected": "BaseLanguageModel",
"types": [
"BaseLanguageModel"
],
"types": ["BaseLanguageModel"],
"value": "__UNDEFINED__"
}
],
@ -697,11 +624,7 @@
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Text",
"Data",
"Prompt"
],
"input_types": ["Text", "Data", "Prompt"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -721,9 +644,7 @@
"fileTypes": [],
"file_path": "",
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -743,9 +664,7 @@
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -765,9 +684,7 @@
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": true,
"load_from_db": false,
"multiline": false,
@ -785,7 +702,7 @@
"show": true,
"title_case": false,
"type": "str",
"value": "gpt-4o"
"value": "gpt-3.5-turbo"
},
"openai_api_base": {
"advanced": true,
@ -794,9 +711,7 @@
"fileTypes": [],
"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.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -816,9 +731,7 @@
"fileTypes": [],
"file_path": "",
"info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": true,
"multiline": false,
@ -838,9 +751,7 @@
"fileTypes": [],
"file_path": "",
"info": "Stream the response from the model. Streaming works only in Chat.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -860,9 +771,7 @@
"fileTypes": [],
"file_path": "",
"info": "System message to pass to the model.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -882,9 +791,7 @@
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -920,9 +827,7 @@
"data": {
"id": "URL-2cX90",
"node": {
"base_classes": [
"Record"
],
"base_classes": ["Record"],
"beta": false,
"custom_fields": {
"urls": null
@ -942,9 +847,7 @@
"method": "fetch_content",
"name": "data",
"selected": "Data",
"types": [
"Data"
],
"types": ["Data"],
"value": "__UNDEFINED__"
}
],
@ -966,7 +869,7 @@
"show": true,
"title_case": false,
"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": {
"advanced": false,
@ -1006,11 +909,7 @@
"data": {
"id": "TextInput-og8Or",
"node": {
"base_classes": [
"object",
"Text",
"str"
],
"base_classes": ["object", "Text", "str"],
"beta": false,
"custom_fields": {
"input_value": null,
@ -1023,16 +922,12 @@
"field_order": [],
"frozen": false,
"icon": "type",
"output_types": [
"Text"
],
"output_types": ["Text"],
"outputs": [
{
"name": "Text",
"selected": "Text",
"types": [
"Text"
]
"types": ["Text"]
}
],
"template": {
@ -1062,10 +957,7 @@
"fileTypes": [],
"file_path": "",
"info": "Text or Record to be passed as input.",
"input_types": [
"Record",
"Text"
],
"input_types": ["Record", "Text"],
"list": false,
"load_from_db": false,
"multiline": false,
@ -1085,9 +977,7 @@
"fileTypes": [],
"file_path": "",
"info": "Template to convert Record to Text. If left empty, it will be dynamically set to the Record's text key.",
"input_types": [
"Text"
],
"input_types": ["Text"],
"list": false,
"load_from_db": false,
"multiline": true,
@ -1131,4 +1021,4 @@
"is_component": false,
"last_tested_version": "1.0.0a0",
"name": "Blog Writer"
}
}

View file

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

View file

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

View file

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

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)):
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)
artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type}
return custom_component, build_result, artifact

View file

@ -1,14 +1,13 @@
import warnings
from datetime import datetime, timedelta, timezone
from typing import Annotated, Coroutine, Optional, Union
from uuid import UUID
import warnings
from cryptography.fernet import Fernet
from fastapi import Depends, HTTPException, Security, status
from fastapi.security import APIKeyHeader, APIKeyQuery, OAuth2PasswordBearer
from jose import JWTError, jwt
from loguru import logger
from sqlmodel import Session
from starlette.websockets import WebSocket
@ -92,44 +91,58 @@ async def get_current_user_by_jwt(
) -> User:
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):
token = await token
if settings_service.auth_settings.SECRET_KEY.get_secret_value() is None:
raise credentials_exception
secret_key = settings_service.auth_settings.SECRET_KEY.get_secret_value()
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:
# Ignore warning about datetime.utcnow
with warnings.catch_warnings():
warnings.simplefilter("ignore")
payload = jwt.decode(
token,
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
payload = jwt.decode(token, secret_key, algorithms=[settings_service.auth_settings.ALGORITHM])
user_id: UUID = payload.get("sub")
token_type: str = payload.get("type")
if expires := payload.get("exp", None):
expires_datetime = datetime.fromtimestamp(expires, timezone.utc)
# TypeError: can't compare offset-naive and offset-aware datetimes
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:
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:
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:
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

View file

@ -6,24 +6,21 @@ from typing import TYPE_CHECKING
import sqlalchemy as sa
from alembic import command, util
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.database import models # noqa
from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults
from langflow.services.deps import get_settings_service
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:
from sqlalchemy.engine import Engine
from langflow.services.settings.service import SettingsService
from sqlalchemy.engine import Engine
class DatabaseService(Service):
@ -48,12 +45,23 @@ class DatabaseService(Service):
connect_args = {"check_same_thread": False}
else:
connect_args = {}
return create_engine(
self.database_url,
connect_args=connect_args,
pool_size=self.settings_service.settings.pool_size,
max_overflow=self.settings_service.settings.max_overflow,
)
try:
return create_engine(
self.database_url,
connect_args=connect_args,
pool_size=self.settings_service.settings.pool_size,
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")
def on_connection(dbapi_connection, connection_record):

View file

@ -91,9 +91,17 @@ class MessageModel(DefaultModel):
files: list[str] = []
@field_validator("files", mode="before")
@classmethod
def validate_files(cls, v):
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
@classmethod

View file

@ -3,12 +3,11 @@ from pathlib import Path
from typing import TYPE_CHECKING, List, Optional, Union
import duckdb
from loguru import logger
from platformdirs import user_cache_dir
from langflow.services.base import Service
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 loguru import logger
from platformdirs import user_cache_dir
if TYPE_CHECKING:
from langflow.services.settings.manager import SettingsService
@ -141,7 +140,7 @@ class MonitorService(Service):
order: Optional[str] = "DESC",
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 = []
if sender:
conditions.append(f"sender = '{sender}'")

View file

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

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow-base"
version = "0.0.63"
version = "0.0.68"
description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"]
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",
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
},
"simple-git-hooks": {
"pre-commit": "npx pretty-quick --staged"
},
"eslintConfig": {
"extends": [
"react-app",
@ -130,7 +127,6 @@
"prettier": "^2.8.8",
"prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.3.0",
"pretty-quick": "^3.1.3",
"simple-git-hooks": "^2.11.1",
"tailwindcss": "^3.3.3",
"tailwindcss-dotted-background": "^1.1.0",
@ -138,4 +134,4 @@
"ua-parser-js": "^1.0.37",
"vite": "^4.5.2"
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -70,7 +70,10 @@ export default function AddNewVariableButton({
let responseError = error as ResponseErrorDetailAPI;
setErrorData({
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 { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const navigate = useNavigate();
const removeFlow = useFlowsManagerStore((store) => store.removeFlow);
const hasStore = useStoreStore((state) => state.hasStore);
@ -208,7 +209,7 @@ export default function Header(): JSX.Element {
0,
BACKEND_URL.length - 1
)}${BASE_URL_API}files/profile_pictures/${
userData?.profile_image ?? "Space/046-rocket.png"
userData?.profile_image ?? "Space/046-rocket.svg"
}` ?? profileCircle
}
className="h-7 w-7 shrink-0 focus-visible:outline-0"
@ -226,7 +227,7 @@ export default function Header(): JSX.Element {
0,
BACKEND_URL.length - 1
)}${BASE_URL_API}files/profile_pictures/${
userData?.profile_image
userData?.profile_image ?? "Space/046-rocket.svg"
}` ?? profileCircle
}
className="h-5 w-5 focus-visible:outline-0 "

View file

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

View file

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

View file

@ -16,7 +16,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
);
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 triggerFolderChange = (folderId) => {
@ -118,7 +118,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
setFolderIdDragging("");
updateFlowInDatabase(updatedFlow).then(() => {
getFoldersApi(true);
refreshFolders();
triggerFolderChange(folderId);
});
};
@ -129,7 +129,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
setFolderDragging(false);
setFolderIdDragging("");
uploadFlowsFromFolders(formData).then(() => {
getFoldersApi(true);
refreshFolders();
triggerFolderChange(folderId);
});
};

View file

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

View file

@ -105,7 +105,7 @@ const TableComponent = forwardRef<
}
}, 50);
setTimeout(() => {
realRef.current.api.hideOverlay();
realRef?.current?.api?.hideOverlay();
}, 1000);
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 =
"Please ensure your file has one of the following extensions:";
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 { getLoggedUser, requestLogout } from "../controllers/API";
import useAlertStore from "../stores/alertStore";
import { useFolderStore } from "../stores/foldersStore";
import { Users } from "../types/api";
import { AuthContextType } from "../types/contexts/auth";
@ -43,6 +44,8 @@ export function AuthProvider({ children }): React.ReactElement {
cookies.get("apikey_tkn_lflw")
);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
useEffect(() => {
const storedAccessToken = cookies.get("access_token_lf");
if (storedAccessToken) {
@ -59,11 +62,12 @@ export function AuthProvider({ children }): React.ReactElement {
function getUser() {
getLoggedUser()
.then((user) => {
.then(async (user) => {
setUserData(user);
setLoading(false);
const isSuperUser = user!.is_superuser;
setIsAdmin(isSuperUser);
getFoldersApi(true, true);
})
.catch((error) => {
setLoading(false);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -36,12 +36,6 @@ export default function ChatView({
const outputTypes = outputs.map((obj) => obj.type);
const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
// useEffect(() => {
// if (!outputTypes.includes("ChatOutput")) {
// setNoticeData({ title: NOCHATOUTPUT_NOTICE_ALERT });
// }
// }, []);
//build chat history
useEffect(() => {
const chatOutputResponses: VertexBuildTypeAPI[] = [];
@ -62,14 +56,24 @@ export default function ChatView({
const chatMessages: ChatMessageType[] = chatOutputResponses
.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) => {
try {
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 } =
output.data.message;
console.log("output.data.message:", output.data.message);
console.log("output.data.message.files:", output.data.message.files);
hasMessageValue ? output.data.message : output.artifacts;
const is_ai =
sender === "Machine" || sender === null || sender === undefined;
return {
@ -136,26 +140,12 @@ export default function ChatView({
message: string,
stream_url?: string
) {
// if (message === "") return;
chat.message = message;
// chat is one of the chatHistory
updateFlowPool(chat.componentId, {
message,
sender_name: chat.sender_name ?? "Bot",
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 [isDragging, setIsDragging] = useState(false);
@ -190,44 +180,6 @@ export default function ChatView({
aria-hidden="true"
/>
</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 ref={messagesRef} className="chat-message-div">
{chatHistory?.length > 0 ? (

View file

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

View file

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

View file

@ -11,10 +11,10 @@ export const getNodesWithDefaultValue = (flow) => {
.filter(
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show &&
node.data.node.template[templateField]?.show &&
LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField].type
)
node.data.node.template[templateField].type,
),
)
.map((n, i) => {
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";
export const getValue = (
value: string,
node: NodeType,
template: InputFieldType,
tweak: Object[]
template: TemplateVariableType,
tweak: Object[],
) => {
let returnValue = value ?? "";

View file

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

View file

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

View file

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

View file

@ -57,17 +57,17 @@ export default function NodeToolbarComponent({
const nodeLength = Object.keys(data.node!.template).filter(
(templateField) =>
templateField.charAt(0) !== "_" &&
data.node?.template[templateField].show &&
(data.node.template[templateField].type === "str" ||
data.node.template[templateField].type === "bool" ||
data.node.template[templateField].type === "float" ||
data.node.template[templateField].type === "code" ||
data.node.template[templateField].type === "prompt" ||
data.node.template[templateField].type === "file" ||
data.node.template[templateField].type === "Any" ||
data.node.template[templateField].type === "int" ||
data.node.template[templateField].type === "dict" ||
data.node.template[templateField].type === "NestedDict")
data.node?.template[templateField]?.show &&
(data.node.template[templateField]?.type === "str" ||
data.node.template[templateField]?.type === "bool" ||
data.node.template[templateField]?.type === "float" ||
data.node.template[templateField]?.type === "code" ||
data.node.template[templateField]?.type === "prompt" ||
data.node.template[templateField]?.type === "file" ||
data.node.template[templateField]?.type === "Any" ||
data.node.template[templateField]?.type === "int" ||
data.node.template[templateField]?.type === "dict" ||
data.node.template[templateField]?.type === "NestedDict")
).length;
const hasStore = useStoreStore((state) => state.hasStore);
@ -626,7 +626,7 @@ export default function NodeToolbarComponent({
/>
</SelectItem>
)}
{(!hasStore || !hasApiKey || !validApiKey) && (
{/* {(!hasStore || !hasApiKey || !validApiKey) && (
<SelectItem value={"Download"}>
<ToolbarSelectItem
shortcut={
@ -638,7 +638,7 @@ export default function NodeToolbarComponent({
dataTestId="Download-button-modal"
/>
</SelectItem>
)}
)} */}
<SelectItem
value={"documentation"}
disabled={data.node?.documentation === ""}
@ -688,16 +688,19 @@ export default function NodeToolbarComponent({
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
<SelectItem value="Download">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Download")?.shortcut!
}
value={"Download"}
icon={"Download"}
dataTestId="download-button-modal"
/>
</SelectItem>
{(!hasStore || !hasApiKey || !validApiKey) && (
<SelectItem value="Download">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Download")
?.shortcut!
}
value={"Download"}
icon={"Download"}
dataTestId="download-button-modal"
/>
</SelectItem>
)}
<SelectItem
value={"delete"}
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 { onLogin } from "../../controllers/API";
import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { LoginType } from "../../types/api";
import {
inputHandlerEventType,
@ -20,10 +21,10 @@ export default function LoginPage(): JSX.Element {
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
const { password, username } = inputState;
const { login, isAuthenticated, setUserData, setIsAdmin } =
useContext(AuthContext);
const { login } = useContext(AuthContext);
const navigate = useNavigate();
const setErrorData = useAlertStore((state) => state.setErrorData);
const setLoading = useFlowsManagerStore((state) => state.setIsLoading);
function handleInput({
target: { name, value },
@ -38,6 +39,9 @@ export default function LoginPage(): JSX.Element {
};
onLogin(user)
.then((user) => {
console.log("login page");
setLoading(true);
login(user.access_token);
navigate("/");
})

View file

@ -1,39 +1,19 @@
import { useState } from "react";
import { useLocation } from "react-router-dom";
import useAlertStore from "../../../../../../stores/alertStore";
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
import { useFolderStore } from "../../../../../../stores/foldersStore";
import { handleDownloadFolderFn } from "../../../../utils/handle-download-folder";
import InputSearchComponent from "../inputSearchComponent";
import TabsSearchComponent from "../tabsComponent";
type 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 [tabActive, setTabActive] = useState("Flows");
const setErrorData = useAlertStore((state) => state.setErrorData);
const allFlows = useFlowsManagerStore((state) => state.allFlows);
const [inputValue, setInputValue] = useState("");
const setSearchFlowsComponents = useFlowsManagerStore(
(state) => state.setSearchFlowsComponents
);
const handleDownloadFolder = () => {
if (allFlows.length === 0) {
setErrorData({
title: "Folder is empty",
list: [],
});
return;
}
handleDownloadFolderFn(folderId);
};
return (
<>
<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 { deleteFolder, getFolderById } from "../services";
const useDeleteFolder = ({ navigate, getFoldersApi }) => {
const useDeleteFolder = ({ navigate }) => {
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const folderToEdit = useFolderStore((state) => state.folderToEdit);
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const handleDeleteFolder = () => {
deleteFolder(folderToEdit?.id!)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
from datetime import datetime
import pytest
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.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
response = client.get("/api/v1/users/whoami", headers=logged_in_headers)
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):