Merge remote-tracking branch 'origin/dev' into node-shortcuts-refactor

This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-04-12 11:56:01 -03:00
commit 23bcdcec28
216 changed files with 11018 additions and 10942 deletions

View file

@ -71,6 +71,10 @@ LANGFLOW_SUPERUSER=
# Example: LANGFLOW_SUPERUSER_PASSWORD=123456
LANGFLOW_SUPERUSER_PASSWORD=
# Should store environment variables in the database
# Values: true, false
LANGFLOW_STORE_ENVIRONMENT_VARIABLES=
# STORE_URL
# Example: LANGFLOW_STORE_URL=https://api.langflow.store
# LANGFLOW_STORE_URL=

View file

@ -0,0 +1,94 @@
# An action for setting up poetry install with caching.
# Using a custom action since the default action does not
# take poetry install groups into account.
# Action code from:
# https://github.com/actions/setup-python/issues/505#issuecomment-1273013236
# Copy of https://github.com/langchain-ai/langchain/blob/2f8dd1a1619f25daa4737df4d378b1acd6ff83c4/.github/actions/poetry_setup/action.yml
name: poetry-install-with-caching
description: Poetry install with support for caching of dependency groups.
inputs:
python-version:
description: Python version, supporting MAJOR.MINOR only
required: true
poetry-version:
description: Poetry version
required: true
cache-key:
description: Cache key to use for manual handling of caching
required: true
working-directory:
description: Directory whose poetry.lock file should be cached
required: true
runs:
using: composite
steps:
- uses: actions/setup-python@v5
name: Setup python ${{ inputs.python-version }}
id: setup-python
with:
python-version: ${{ inputs.python-version }}
- uses: actions/cache@v4
id: cache-bin-poetry
name: Cache Poetry binary - Python ${{ inputs.python-version }}
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "1"
with:
path: |
/opt/pipx/venvs/poetry
# This step caches the poetry installation, so make sure it's keyed on the poetry version as well.
key: bin-poetry-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-${{ inputs.poetry-version }}
- name: Refresh shell hashtable and fixup softlinks
if: steps.cache-bin-poetry.outputs.cache-hit == 'true'
shell: bash
env:
POETRY_VERSION: ${{ inputs.poetry-version }}
PYTHON_VERSION: ${{ inputs.python-version }}
run: |
set -eux
# Refresh the shell hashtable, to ensure correct `which` output.
hash -r
# `actions/cache@v3` doesn't always seem able to correctly unpack softlinks.
# Delete and recreate the softlinks pipx expects to have.
rm /opt/pipx/venvs/poetry/bin/python
cd /opt/pipx/venvs/poetry/bin
ln -s "$(which "python$PYTHON_VERSION")" python
chmod +x python
cd /opt/pipx_bin/
ln -s /opt/pipx/venvs/poetry/bin/poetry poetry
chmod +x poetry
# Ensure everything got set up correctly.
/opt/pipx/venvs/poetry/bin/python --version
/opt/pipx_bin/poetry --version
- name: Install poetry
if: steps.cache-bin-poetry.outputs.cache-hit != 'true'
shell: bash
env:
POETRY_VERSION: ${{ inputs.poetry-version }}
PYTHON_VERSION: ${{ inputs.python-version }}
# Install poetry using the python version installed by setup-python step.
run: pipx install "poetry==$POETRY_VERSION" --python '${{ steps.setup-python.outputs.python-path }}' --verbose
- name: Restore pip and poetry cached dependencies
uses: actions/cache@v4
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "4"
WORKDIR: ${{ inputs.working-directory == '' && '.' || inputs.working-directory }}
with:
path: |
~/.cache/pip
~/.cache/pypoetry/virtualenvs
~/.cache/pypoetry/cache
~/.cache/pypoetry/artifacts
${{ env.WORKDIR }}/.venv
key: py-deps-${{ runner.os }}-${{ runner.arch }}-py-${{ inputs.python-version }}-poetry-${{ inputs.poetry-version }}-${{ inputs.cache-key }}-${{ hashFiles(format('{0}/**/poetry.lock', env.WORKDIR)) }}

View file

@ -1,44 +0,0 @@
name: "Async API tests"
on:
push:
branches:
- dev
pull_request:
branches:
- dev
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Set up Docker
run: docker --version && docker-compose --version
- name: "Create env file"
working-directory: ./deploy
run: |
echo "${{ secrets.ENV_FILE }}" > .env
- name: Build and start services
working-directory: ./deploy
run: docker compose up --exit-code-from tests tests result_backend broker celeryworker db --build
continue-on-error: true
# - name: Stop services
# run: docker compose down

View file

@ -26,20 +26,24 @@ jobs:
- "3.11"
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: |
pipx install poetry==$POETRY_VERSION
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
id: setup-python
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_caching"
with:
python-version: ${{ matrix.python-version }}
cache: poetry
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
if: ${{ steps.setup-python.outputs.cache-hit != 'true' }}
- name: Analysing the code with our lint
- name: Get .mypy_cache to speed up mypy
uses: actions/cache@v4
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: "2"
with:
path: |
./.mypy_cache
key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }}
- name: Lint check
run: |
make lint

View file

@ -1,26 +1,20 @@
name: Langflow Base Pre-release
run-name: Langflow Base Pre-release by @${{ github.actor }}
on:
pull_request:
types:
- closed
branches:
- dev
paths:
- "src/backend/base/pyproject.toml"
workflow_dispatch:
inputs:
force_release:
description: "Force a release"
required: false
default: "false"
release_package:
description: "Release package"
required: true
type: boolean
default: false
env:
POETRY_VERSION: "1.8.2"
jobs:
if_release:
if: ${{ (github.event.pull_request.merged == true) && contains(github.event.pull_request.labels.*.name, 'pre-release') }} || ${{ github.event_name == 'workflow_dispatch' && inputs.force_release == 'true' }}
if: inputs.release_package == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -50,22 +44,11 @@ jobs:
fi
- name: Build project for distribution
run: make build base=true
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
run: |
make publish base=true
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: true
prerelease: true
tag: v${{ steps.check-version.outputs.version }}
commit: dev
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx

View file

@ -1,19 +1,13 @@
name: Langflow Pre-release
run-name: Langflow Pre-release by @${{ github.actor }}
on:
pull_request:
types:
- closed
branches:
- dev
paths:
- "pyproject.toml"
workflow_dispatch:
inputs:
force_release:
description: "Force a release"
required: false
default: "false"
release_package:
description: "Release package"
required: true
type: boolean
default: false
workflow_run:
workflows: ["pre-release-base"]
types: [completed]
@ -24,7 +18,7 @@ env:
jobs:
if_release:
if: ${{ (github.event.pull_request.merged == true) && contains(github.event.pull_request.labels.*.name, 'pre-release') }} || ${{ github.event_name == 'workflow_dispatch' && inputs.force_release == 'true' }}
if: inputs.release_package == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -38,7 +32,7 @@ jobs:
- name: Check Version
id: check-version
run: |
version=$(cd src/backend/base && poetry version --short)
version=$(poetry version --short)
last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1)
if [ "$version" = "$last_released_version" ]; then
echo "Version $version is already released. Skipping release."
@ -55,16 +49,6 @@ jobs:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
run: |
make publish main=true
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: true
prerelease: true
tag: v${{ steps.check-version.outputs.version }}
commit: dev
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
@ -83,3 +67,14 @@ jobs:
tags: |
logspace/langflow:${{ steps.check-version.outputs.version }}
logspace/langflow:1.0-alpha
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: true
prerelease: true
tag: v${{ steps.check-version.outputs.version }}
commit: dev

View file

@ -29,19 +29,16 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pipx install poetry==$POETRY_VERSION
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
id: setup-python
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_caching"
with:
python-version: ${{ matrix.python-version }}
cache: "poetry"
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
if: ${{ steps.setup-python.outputs.cache-hit != 'true' }}
- name: Run unit tests
run: |
make tests
make tests args="-n auto"

View file

@ -31,15 +31,6 @@ jobs:
id: check-version
run: |
echo version=$(poetry version --short) >> $GITHUB_OUTPUT
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: true
tag: v${{ steps.check-version.outputs.version }}
commit: main
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
@ -63,3 +54,12 @@ jobs:
tags: |
logspace/langflow:${{ steps.check-version.outputs.version }}
logspace/langflow:latest
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: true
tag: v${{ steps.check-version.outputs.version }}
commit: main

3
.gitignore vendored
View file

@ -264,4 +264,5 @@ scratchpad*
chroma*/*
stuff/*
src/frontend/playwright-report/index.html
*.bak
*.bak
prof/*

View file

@ -16,12 +16,12 @@ The branch structure is as follows:
## 🚩GitHub Issues
Our [issues](https://github.com/logspace-ai/langflow/issues) page is kept up to date
Our [issues](https://github.com/langflow-ai/langflow/issues) page is kept up to date
with bugs, improvements, and feature requests. There is a taxonomy of labels to help
with sorting and discovery of issues of interest.
If you're looking for help with your code, consider posting a question on the
[GitHub Discussions board](https://github.com/logspace-ai/langflow/discussions). Please
[GitHub Discussions board](https://github.com/langflow-ai/langflow/discussions). Please
understand that we won't be able to provide individual support via email. We
also believe that help is much more valuable if it's **shared publicly**,
so that more people can benefit from it.
@ -40,7 +40,7 @@ so that more people can benefit from it.
## Issue labels
[See this page](https://github.com/logspace-ai/langflow/labels) for an overview of
[See this page](https://github.com/langflow-ai/langflow/labels) for an overview of
the system we use to tag our issues and pull requests.
## Local development

View file

@ -4,25 +4,27 @@ This guide will help you set up a Langflow development VM in a Google Cloud Plat
> **Note**: When Cloud Shell opens, be sure to select **Trust repo**. Some `gcloud` commands might not run in an ephemeral Cloud Shell environment.
## Standard VM
## Standard VM
[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial.md)
[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/langflow-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial.md)
This script sets up a Debian-based VM with the Langflow package, Nginx, and the necessary configurations to run the Langflow Dev environment.
<hr>
## Spot/Preemptible Instance
[![Open in Cloud Shell - Spot Instance](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial_spot.md)
[![Open in Cloud Shell - Spot Instance](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/langflow-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial_spot.md)
When running as a [spot (preemptible) instance](https://cloud.google.com/compute/docs/instances/preemptible), the code and VM will behave the same way as in a regular instance, executing the startup script to configure the environment, install necessary dependencies, and run the Langflow application. However, **due to the nature of spot instances, the VM may be terminated at any time if Google Cloud needs to reclaim the resources**. This makes spot instances suitable for fault-tolerant, stateless, or interruptible workloads that can handle unexpected terminations and restarts.
## Pricing (approximate)
> For a more accurate breakdown of costs, please use the [**GCP Pricing Calculator**](https://cloud.google.com/products/calculator)
<br>
| Component | Regular Cost (Hourly) | Regular Cost (Monthly) | Spot/Preemptible Cost (Hourly) | Spot/Preemptible Cost (Monthly) | Notes |
| -------------- | --------------------- | ---------------------- | ------------------------------ | ------------------------------- | ----- |
| 100 GB Disk | - | $10/month | - | $10/month | Disk cost remains the same for both regular and Spot/Preemptible VMs |
| VM (n1-standard-4) | $0.15/hr | ~$108/month | ~$0.04/hr | ~$29/month | The VM cost can be significantly reduced using a Spot/Preemptible instance |
| **Total** | **$0.15/hr** | **~$118/month** | **~$0.04/hr** | **~$39/month** | Total costs for running the VM and disk 24/7 for an entire month |
> For a more accurate breakdown of costs, please use the [**GCP Pricing Calculator**](https://cloud.google.com/products/calculator)
> <br>
| Component | Regular Cost (Hourly) | Regular Cost (Monthly) | Spot/Preemptible Cost (Hourly) | Spot/Preemptible Cost (Monthly) | Notes |
| ------------------ | --------------------- | ---------------------- | ------------------------------ | ------------------------------- | -------------------------------------------------------------------------- |
| 100 GB Disk | - | $10/month | - | $10/month | Disk cost remains the same for both regular and Spot/Preemptible VMs |
| VM (n1-standard-4) | $0.15/hr | ~$108/month | ~$0.04/hr | ~$29/month | The VM cost can be significantly reduced using a Spot/Preemptible instance |
| **Total** | **$0.15/hr** | **~$118/month** | **~$0.04/hr** | **~$39/month** | Total costs for running the VM and disk 24/7 for an entire month |

View file

@ -8,6 +8,14 @@ env ?= .env
open_browser ?= true
path = src/backend/base/langflow/frontend
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
@ -39,20 +47,16 @@ coverage:
# allow passing arguments to pytest
tests:
@make install_backend
poetry run pytest tests --instafail $(args)
# Use like:
format:
poetry run ruff . --fix
poetry run ruff check . --fix
poetry run ruff format .
cd src/frontend && npm run format
lint:
make install_backend
poetry run mypy --namespace-packages -p "langflow"
poetry run ruff . --fix
install_frontend:
cd src/frontend && npm install
@ -129,12 +133,12 @@ frontendc:
make run_frontend
install_backend:
@echo 'Setting up the environment'
@make setup_env
@echo 'Installing backend dependencies'
@poetry install --extras deploy
@poetry install
backend:
@echo 'Setting up the environment'
@make setup_env
make install_backend
@-kill -9 `lsof -t -i:7860`
ifdef login

View file

@ -1,9 +1,11 @@
<!-- markdownlint-disable MD030 -->
# [![Langflow](https://github.com/logspace-ai/langflow/blob/dev/docs/static/img/hero.png)](https://www.langflow.org)
# [![Langflow](https://github.com/langflow-ai/langflow/blob/dev/docs/static/img/hero.png)](https://www.langflow.org)
### [Langflow](https://www.langflow.org) is a new, visual way to build, iterate and deploy AI apps.
# ⚡️ Documentation and Community
- [Documentation](https://docs.langflow.org)
- [Discord](https://discord.com/invite/EqksyE2EX9)
@ -26,7 +28,7 @@ Then, run Langflow with:
python -m langflow run
```
You can also preview Langflow in [HuggingFace Spaces](https://huggingface.co/spaces/Logspace/Langflow-Preview). [Clone the space using this link](https://huggingface.co/spaces/Logspace/Langflow-Preview?duplicate=true), to create your own Langflow workspace in minutes.
You can also preview Langflow in [HuggingFace Spaces](https://huggingface.co/spaces/Langflow/Langflow-Preview). [Clone the space using this link](https://huggingface.co/spaces/Langflow/Langflow-Preview?duplicate=true), to create your own Langflow workspace in minutes.
# 🎨 Creating Flows
@ -94,7 +96,7 @@ Follow our step-by-step guide to deploy Langflow on Google Cloud Platform (GCP)
Alternatively, click the **"Open in Cloud Shell"** button below to launch Google Cloud Shell, clone the Langflow repository, and start an **interactive tutorial** that will guide you through the process of setting up the necessary resources and deploying Langflow on your GCP project.
[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts/gcp&shellonly=true&tutorial=walkthroughtutorial_spot.md)
[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/langflow-ai/langflow&working_dir=scripts/gcp&shellonly=true&tutorial=walkthroughtutorial_spot.md)
## Deploy on Railway
@ -102,7 +104,7 @@ Alternatively, click the **"Open in Cloud Shell"** button below to launch Google
## Deploy on Render
<a href="https://render.com/deploy?repo=https://github.com/logspace-ai/langflow/tree/main">
<a href="https://render.com/deploy?repo=https://github.com/langflow-ai/langflow/tree/main">
<img src="https://render.com/images/deploy-to-render-button.svg" alt="Deploy to Render" />
</a>
@ -112,12 +114,12 @@ We welcome contributions from developers of all levels to our open-source projec
---
[![Star History Chart](https://api.star-history.com/svg?repos=logspace-ai/langflow&type=Timeline)](https://star-history.com/#logspace-ai/langflow&Date)
[![Star History Chart](https://api.star-history.com/svg?repos=langflow-ai/langflow&type=Timeline)](https://star-history.com/#langflow-ai/langflow&Date)
# 🌟 Contributors
[![langflow contributors](https://contrib.rocks/image?repo=logspace-ai/langflow)](https://github.com/logspace-ai/langflow/graphs/contributors)
[![langflow contributors](https://contrib.rocks/image?repo=langflow-ai/langflow)](https://github.com/langflow-ai/langflow/graphs/contributors)
# 📄 License
Langflow is released under the MIT License. See the LICENSE file for details.
Langflow is released under the MIT License. See the [LICENSE](LICENSE) file for details.

View file

@ -57,7 +57,7 @@ RUN apt-get update \
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
# The --mount will mount the buildx cache directory to where
# Poetry and Pip store their cache so that they can re-use it
# Poetry and Pip store their cache so that they can reuse it
RUN --mount=type=cache,target=/root/.cache \
curl -sSL https://install.python-poetry.org | python3 -

View file

@ -56,7 +56,7 @@ RUN apt-get update \
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
# The --mount will mount the buildx cache directory to where
# Poetry and Pip store their cache so that they can re-use it
# Poetry and Pip store their cache so that they can reuse it
RUN --mount=type=cache,target=/root/.cache \
curl -sSL https://install.python-poetry.org | python3 -

View file

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

View file

@ -0,0 +1,3 @@
FROM logspace/langflow:1.0-alpha
CMD ["python", "-m", "langflow", "run", "--host", "0.0.0.0", "--port", "7860"]

View file

@ -0,0 +1,30 @@
version: "3.8"
services:
langflow:
image: logspace/langflow:1.0-alpha
ports:
- "7860:7860"
depends_on:
- postgres
environment:
- LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@postgres:5432/langflow
# This variable defines where the logs, file storage, monitor data and secret keys are stored.
- LANGFLOW_CONFIG_DIR=/var/lib/langflow
volumes:
- langflow-data:/var/lib/langflow
postgres:
image: postgres:16
environment:
POSTGRES_USER: langflow
POSTGRES_PASSWORD: langflow
POSTGRES_DB: langflow
ports:
- "5432:5432"
volumes:
- langflow-postgres:/var/lib/postgresql/data
volumes:
langflow-postgres:
langflow-data:

View file

@ -37,7 +37,7 @@ The CustomComponent class serves as the foundation for creating custom component
| _`langflow.field_typing.Prompt`_ |
| _`langchain.chains.base.Chain`_ |
| _`langchain.PromptTemplate`_ |
| _`langchain.llms.base.BaseLLM`_ |
| _`from langchain.schema.language_model import BaseLanguageModel`_ |
| _`langchain.Tool`_ |
| _`langchain.document_loaders.base.BaseLoader`_ |
| _`langchain.schema.Document`_ |

View file

@ -123,7 +123,12 @@ Used to load [OpenAIs](https://openai.com/) embedding models.
Wrapper around [Google Vertex AI](https://cloud.google.com/vertex-ai) [Embeddings API](https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings/get-text-embeddings).
<Admonition type="info">
Vertex AI is a cloud computing platform offered by Google Cloud Platform (GCP). It provides access, management, and development of applications and services through global data centers. To use Vertex AI PaLM, you need to have the [google-cloud-aiplatform](https://pypi.org/project/google-cloud-aiplatform/) Python package installed and credentials configured for your environment.
Vertex AI is a cloud computing platform offered by Google Cloud Platform
(GCP). It provides access, management, and development of applications and
services through global data centers. To use Vertex AI PaLM, you need to have
the
[google-cloud-aiplatform](https://pypi.org/project/google-cloud-aiplatform/)
Python package installed and credentials configured for your environment.
</Admonition>
- **credentials:** The default custom credentials (google.auth.credentials.Credentials) to use.

View file

@ -1,4 +1,4 @@
import Admonition from '@theme/Admonition';
import Admonition from "@theme/Admonition";
# Models
@ -13,16 +13,17 @@ This component facilitates the generation of text using the LLM (Large Language
- **System Message (Optional):** A system message to pass to the model.
- **Model ID (Optional):** Specifies the model ID to be used for text generation. Defaults to _`"anthropic.claude-instant-v1"`_. Available options include:
- _`"ai21.j2-grande-instruct"`_
- _`"ai21.j2-jumbo-instruct"`_
- _`"ai21.j2-mid"`_
- _`"ai21.j2-mid-v1"`_
- _`"ai21.j2-ultra"`_
- _`"ai21.j2-ultra-v1"`_
- _`"anthropic.claude-instant-v1"`_
- _`"anthropic.claude-v1"`_
- _`"anthropic.claude-v2"`_
- _`"cohere.command-text-v14"`_
- _`"ai21.j2-grande-instruct"`_
- _`"ai21.j2-jumbo-instruct"`_
- _`"ai21.j2-mid"`_
- _`"ai21.j2-mid-v1"`_
- _`"ai21.j2-ultra"`_
- _`"ai21.j2-ultra-v1"`_
- _`"anthropic.claude-instant-v1"`_
- _`"anthropic.claude-v1"`_
- _`"anthropic.claude-v2"`_
- _`"cohere.command-text-v14"`_
- **Credentials Profile Name (Optional):** Specifies the name of the credentials profile.
@ -39,12 +40,12 @@ This component facilitates the generation of text using the LLM (Large Language
- **Stream (Optional):** Specifies whether to stream the response from the model. Defaults to _`False`_.
<Admonition type="note" title="Note">
<p>
Ensure that necessary credentials are provided to connect to the Amazon Bedrock API. If connection fails, a ValueError will be raised.
</p>
<p>
Ensure that necessary credentials are provided to connect to the Amazon
Bedrock API. If connection fails, a ValueError will be raised.
</p>
</Admonition>
---
### Anthropic
@ -54,10 +55,11 @@ This component allows the generation of text using Anthropic Chat&Completion lar
**Params**
- **Model Name:** Specifies the name of the Anthropic model to be used for text generation. Available options include:
- _`"claude-2.1"`_
- _`"claude-2.0"`_
- _`"claude-instant-1.2"`_
- _`"claude-instant-1"`_
- _`"claude-2.1"`_
- _`"claude-2.0"`_
- _`"claude-instant-1.2"`_
- _`"claude-instant-1"`_
- **Anthropic API Key:** Your Anthropic API key.
@ -84,25 +86,27 @@ This component allows the generation of text using the LLM (Large Language Model
**Params**
- **Model Name:** Specifies the name of the Azure OpenAI model to be used for text generation. Available options include:
- _`"gpt-35-turbo"`_
- _`"gpt-35-turbo-16k"`_
- _`"gpt-35-turbo-instruct"`_
- _`"gpt-4"`_
- _`"gpt-4-32k"`_
- _`"gpt-4-vision"`_
- _`"gpt-35-turbo"`_
- _`"gpt-35-turbo-16k"`_
- _`"gpt-35-turbo-instruct"`_
- _`"gpt-4"`_
- _`"gpt-4-32k"`_
- _`"gpt-4-vision"`_
- **Azure Endpoint:** Your Azure endpoint, including the resource. Example: `https://example-resource.azure.openai.com/`.
- **Deployment Name:** Specifies the name of the deployment.
- **API Version:** Specifies the version of the Azure OpenAI API to be used. Available options include:
- _`"2023-03-15-preview"`_
- _`"2023-05-15"`_
- _`"2023-06-01-preview"`_
- _`"2023-07-01-preview"`_
- _`"2023-08-01-preview"`_
- _`"2023-09-01-preview"`_
- _`"2023-12-01-preview"`_
- _`"2023-03-15-preview"`_
- _`"2023-05-15"`_
- _`"2023-06-01-preview"`_
- _`"2023-07-01-preview"`_
- _`"2023-08-01-preview"`_
- _`"2023-09-01-preview"`_
- _`"2023-12-01-preview"`_
- **API Key:** Your Azure OpenAI API key.
@ -118,7 +122,6 @@ This component allows the generation of text using the LLM (Large Language Model
For detailed documentation and integration guides, please refer to the [Azure OpenAI Component Documentation](https://python.langchain.com/docs/integrations/llms/azure_openai).
---
### Cohere
@ -265,7 +268,7 @@ This component facilitates text generation using OpenAI's models.
- **OpenAI API Base (Optional):** The base URL of the OpenAI API. Defaults to _`https://api.openai.com/v1`_.
- **OpenAI API Key (Optional):** The API key for accessing the OpenAI API.
- **OpenAI API Key (Optional):** The API key for accessing the OpenAI API.
- **Temperature:** Controls the creativity of model responses. Defaults to _`0.7`_.
@ -282,16 +285,17 @@ This component facilitates the generation of text using Baidu Qianfan chat model
**Params**
- **Model Name:** Specifies the name of the Qianfan chat model to be used for text generation. Available options include:
- _`"ERNIE-Bot"`_
- _`"ERNIE-Bot-turbo"`_
- _`"BLOOMZ-7B"`_
- _`"Llama-2-7b-chat"`_
- _`"Llama-2-13b-chat"`_
- _`"Llama-2-70b-chat"`_
- _`"Qianfan-BLOOMZ-7B-compressed"`_
- _`"Qianfan-Chinese-Llama-2-7B"`_
- _`"ChatGLM2-6B-32K"`_
- _`"AquilaChat-7B"`_
- _`"ERNIE-Bot"`_
- _`"ERNIE-Bot-turbo"`_
- _`"BLOOMZ-7B"`_
- _`"Llama-2-7b-chat"`_
- _`"Llama-2-13b-chat"`_
- _`"Llama-2-70b-chat"`_
- _`"Qianfan-BLOOMZ-7B-compressed"`_
- _`"Qianfan-Chinese-Llama-2-7B"`_
- _`"ChatGLM2-6B-32K"`_
- _`"AquilaChat-7B"`_
- **Qianfan Ak:** Your Baidu Qianfan access key, obtainable from [here](https://cloud.baidu.com/product/wenxinworkshop).
@ -343,4 +347,4 @@ The `ChatVertexAI` is a component for generating text using Vertex AI Chat large
- **Stream (Optional):** Specifies whether to stream the response from the model. Defaults to _`False`_.
- **System Message (Optional):** System message to pass to the model.
- **System Message (Optional):** System message to pass to the model.

View file

@ -2,11 +2,11 @@
## 🤖 Join **Langflow** Discord server
Join us to ask questions and showcase your projects.
Join us to ask questions and showcase your projects.
Let's bring together the building blocks of AI integration!
Let's bring together the building blocks of AI integration!
Langflow [Discord](https://discord.gg/EqksyE2EX9) server.
Langflow [Discord](https://discord.gg/EqksyE2EX9) server.
---
@ -15,9 +15,10 @@
Follow [@langflow_ai](https://twitter.com/langflow_ai) on **Twitter** to get the latest news about **Langflow**.
---
## ⭐️ Star **Langflow** on GitHub
You can "star" **Langflow** in [GitHub](https://github.com/logspace-ai/langflow).
You can "star" **Langflow** in [GitHub](https://github.com/langflow-ai/langflow).
By adding a star, other users will be able to find it more easily and see that it has been already useful for others.
@ -25,14 +26,12 @@ By adding a star, other users will be able to find it more easily and see that i
## 👀 Watch the GitHub repository for releases
You can "watch" **Langflow** in [GitHub](https://github.com/logspace-ai/langflow).
You can "watch" **Langflow** in [GitHub](https://github.com/langflow-ai/langflow).
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, discussions, PRs, etc.
Then you can try and help them solve those questions.
---
Thanks! 🚀
Thanks! 🚀

View file

@ -1,11 +1,11 @@
# GitHub Issues
Our [issues](https://github.com/logspace-ai/langflow/issues) page is kept up to date
Our [issues](https://github.com/langflow-ai/langflow/issues) page is kept up to date
with bugs, improvements, and feature requests. There is a taxonomy of labels to help
with sorting and discovery of issues of interest.
If you're looking for help with your code, consider posting a question on the
[GitHub Discussions board](https://github.com/logspace-ai/langflow/discussions). Please
[GitHub Discussions board](https://github.com/langflow-ai/langflow/discussions). Please
understand that we won't be able to provide individual support via email. We
also believe that help is much more valuable if it's **shared publicly**,
so that more people can benefit from it.
@ -21,7 +21,6 @@ so that more people can benefit from it.
logs or tracebacks, you can wrap them in `<details>` and `</details>`. This
[collapses the content](https://developer.mozilla.org/en/docs/Web/HTML/Element/details) so it only becomes visible on click, making the issue easier to read and follow.
## Issue labels
[See this page](https://github.com/logspace-ai/langflow/labels) for an overview of the system we use to tag our issues and pull requests.
[See this page](https://github.com/langflow-ai/langflow/labels) for an overview of the system we use to tag our issues and pull requests.

View file

@ -1,6 +1,6 @@
# How to contribute?
👋 Hello there! We welcome contributions from developers of all levels to our open-source project on [GitHub](https://github.com/logspace-ai/langflow). If you'd like to contribute, please check our contributing guidelines and help make Langflow more accessible.
👋 Hello there! We welcome contributions from developers of all levels to our open-source project on [GitHub](https://github.com/langflow-ai/langflow). If you'd like to contribute, please check our contributing guidelines and help make Langflow more accessible.
As an open-source project in a rapidly developing field, we are extremely open
to contributions, whether in the form of a new feature, improved infra, or better documentation.
@ -10,6 +10,7 @@ To contribute to this project, please follow a ["fork and pull request"](https:/
Please do not try to push directly to this repo unless you are a maintainer.
---
## Local development
You can develop Langflow using docker compose, or locally.
@ -17,6 +18,7 @@ You can develop Langflow using docker compose, or locally.
We provide a .vscode/launch.json file for debugging the backend in VSCode, which is a lot faster than using docker compose.
Setting up hooks:
```bash
make init
```
@ -48,7 +50,6 @@ And the frontend:
make frontend
```
---
## Docker compose

View file

@ -6,10 +6,9 @@ This guide will help you set up a Langflow development VM in a Google Cloud Plat
> Note: When Cloud Shell opens, be sure to select **Trust repo**. Some `gcloud` commands might not run in an ephemeral Cloud Shell environment.
## Standard VM
## Standard VM
[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial.md)
[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/langflow-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial.md)
This script sets up a Debian-based VM with the Langflow package, Nginx, and the necessary configurations to run the Langflow Dev environment.
@ -24,11 +23,11 @@ When running as a [spot (preemptible) instance](https://cloud.google.com/compute
---
## Pricing (approximate)
> For a more accurate breakdown of costs, please use the [**GCP Pricing Calculator**](https://cloud.google.com/products/calculator)
| Component | Regular Cost (Hourly) | Regular Cost (Monthly) | Spot/Preemptible Cost (Hourly) | Spot/Preemptible Cost (Monthly) | Notes |
| -------------- | --------------------- | ---------------------- | ------------------------------ | ------------------------------- | ----- |
| 100 GB Disk | - | $10/month | - | $10/month | Disk cost remains the same for both regular and Spot/Preemptible VMs |
| VM (n1-standard-4) | $0.15/hr | ~$108/month | ~$0.04/hr | ~$29/month | The VM cost can be significantly reduced using a Spot/Preemptible instance |
| **Total** | **$0.15/hr** | **~$118/month** | **~$0.04/hr** | **~$39/month** | Total costs for running the VM and disk 24/7 for an entire month |
| Component | Regular Cost (Hourly) | Regular Cost (Monthly) | Spot/Preemptible Cost (Hourly) | Spot/Preemptible Cost (Monthly) | Notes |
| ------------------ | --------------------- | ---------------------- | ------------------------------ | ------------------------------- | -------------------------------------------------------------------------- |
| 100 GB Disk | - | $10/month | - | $10/month | Disk cost remains the same for both regular and Spot/Preemptible VMs |
| VM (n1-standard-4) | $0.15/hr | ~$108/month | ~$0.04/hr | ~$29/month | The VM cost can be significantly reduced using a Spot/Preemptible instance |
| **Total** | **$0.15/hr** | **~$118/month** | **~$0.04/hr** | **~$39/month** | Total costs for running the VM and disk 24/7 for an entire month |

View file

@ -0,0 +1,195 @@
import ThemedImage from "@theme/ThemedImage";
import useBaseUrl from "@docusaurus/useBaseUrl";
import ZoomableImage from "/src/theme/ZoomableImage.js";
import Admonition from "@theme/Admonition";
# 🌟 RAG with Astra DB
This guide will walk you through how to build a RAG (Retrieval Augmented Generation) application using **Astra DB** and **Langflow**.
[Astra DB](https://www.datastax.com/products/datastax-astra?utm_source=langflow-pre-release&utm_medium=referral&utm_campaign=langflow-announcement&utm_content=astradb) is a cloud-native database built on Apache Cassandra that is optimized for the cloud. It is a fully managed database-as-a-service that simplifies operations and reduces costs. Astra DB is built on the same technology that powers the largest Cassandra deployments in the world.
In this guide, we will use Astra DB as a vector store to store and retrieve the documents that will be used by the RAG application to generate responses.
<Admonition type="tip">
This guide assumes that you have Langflow up and running. If you are new to
Langflow, you can check out the [Getting Started](/) guide.
</Admonition>
TLDR;
- [Create a free Astra DB account](https://astra.datastax.com/signup?utm_source=langflow-pre-release&utm_medium=referral&utm_campaign=langflow-announcement&utm_content=create-a-free-astra-db-account)
- Duplicate our [Langflow 1.0 Space](https://huggingface.co/spaces/Langflow/Langflow-Preview?duplicate=true)
- Create a new database, get a **Token** and the **API Endpoint**
- Click on the **New Project** button and look for Vector Store RAG. This will create a new project with the necessary components
- Import the project into Langflow by dropping it on the Canvas or My Collection page
- Update the **Token** and **API Endpoint** in the **Astra DB** components
- Update the OpenAI API key in the **OpenAI** components
- Run the ingestion flow which is the one that uses the **Astra DB** component
- Click on the ⚡ _Run_ button and start interacting with your RAG application
# First things first
## Create an Astra DB Database
To get started, you will need to [create an Astra DB database](https://astra.datastax.com/signup?utm_source=langflow-pre-release&utm_medium=referral&utm_campaign=langflow-announcement&utm_content=create-an-astradb-database).
Once you have created an account, you will be taken to the Astra DB dashboard. Click on the **Create Database** button.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-create-database.png",
dark: "img/astra-create-database.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
Now you will need to configure your database. Choose the **Serverless (Vector)** deployment type, and pick a Database name, provider and region.
After you have configured your database, click on the **Create Database** button.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-configure-deployment.png",
dark: "img/astra-configure-deployment.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
Once your database is initialized, to the right of the page, you will see the _Database Details_ section which contains a button for you to copy the **API Endpoint** and another to generate a **Token**.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-generate-token.png",
dark: "img/astra-generate-token.png",
}}
style={{ width: "50%", margin: "20px auto" }}
/>
Now we are all set to start building our RAG application using Astra DB and Langflow.
## (Optional) Duplicate the Langflow 1.0 HuggingFace Space
If you haven't already, now is the time to launch Langflow. To make things easier, you can duplicate our [Langflow 1.0 Space](https://huggingface.co/spaces/Langflow/Langflow-Preview?duplicate=true) which sets up a Langflow instance just for you.
## Open the Vector Store RAG Project
To get started, click on the **New Project** button and look for the **Vector Store RAG** project. This will open a starter project with the necessary components to run a RAG application using Astra DB.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/drag-and-drop-flow.png",
dark: "img/drag-and-drop-flow.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
This project consists of two flows. The simpler one is the **Ingestion Flow** which is responsible for ingesting the documents into the Astra DB database.
Your first step should be to understand what each flow does and how they interact with each other.
The ingestion flow consists of:
- **Files** component that uploads a text file to Langflow
- **Recursive Character Text Splitter** component that splits the text into smaller chunks
- **OpenAIEmbeddings** component that generates embeddings for the text chunks
- **Astra DB** component that stores the text chunks in the Astra DB database
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-ingestion-flow.png",
dark: "img/astra-ingestion-flow.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
Now, let's update the **Astra DB** and **Astra DB Search** components with the **Token** and **API Endpoint** that we generated earlier, and the OpenAI Embeddings components with your OpenAI API key.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-ingestion-fields.png",
dark: "img/astra-ingestion-fields.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
And run it! This will ingest the Text data from your file into the Astra DB database.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-ingestion-run.png",
dark: "img/astra-ingestion-run.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
Now, on to the **RAG Flow**. This flow is responsible for generating responses to your queries. It will define all of the steps from getting the User's input to generating a response and displaying it in the Interaction Panel.
The RAG flow is a bit more complex. It consists of:
- **Chat Input** component that defines where to put the user input coming from the Interaction Panel
- **OpenAI Embeddings** component that generates embeddings from the user input
- **Astra DB Search** component that retrieves the most relevant Records from the Astra DB database
- **Text Output** component that turns the Records into Text by concatenating them and also displays it in the Interaction Panel
- One interesting point you'll see here is that this component is named `Extracted Chunks`, and that is how it will appear in the Interaction Panel
- **Prompt** component that takes in the user input and the retrieved Records as text and builds a prompt for the OpenAI model
- **OpenAI** component that generates a response to the prompt
- **Chat Output** component that displays the response in the Interaction Panel
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-rag-flow.png",
dark: "img/astra-rag-flow.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
To run it all we have to do is click on the ⚡ _Run_ button and start interacting with your RAG application.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-rag-flow-run.png",
dark: "img/astra-rag-flow-run.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
This opens the Interaction Panel where you can chat your data.
Because this flow has a **Chat Input** and a **Text Output** component, the Panel displays a chat input at the bottom and the Extracted Chunks section on the left.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-rag-flow-interaction-panel.png",
dark: "img/astra-rag-flow-interaction-panel.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
Once we interact with it we get a response and the Extracted Chunks section is updated with the retrieved records.
<ZoomableImage
alt="Docusaurus themed image"
sources={{
light: "img/astra-rag-flow-interaction-panel-interaction.png",
dark: "img/astra-rag-flow-interaction-panel-interaction.png",
}}
style={{ width: "80%", margin: "20px auto" }}
/>
And that's it! You have successfully ran a RAG application using Astra DB and Langflow.
# Conclusion
In this guide, we have learned how to run a RAG application using Astra DB and Langflow.
We have seen how to create an Astra DB database, import the Astra DB RAG Flows project into Langflow, and run the ingestion and RAG flows.

View file

@ -78,7 +78,7 @@ The Chat Widget can be embedded into any HTML page, inside a _`<body>`_ tag, as
To embed the Chat Widget using React, you'll need to insert this _`<script>`_ tag into the React _index.html_ file, inside the _`<body>`_ tag:
```html
<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/langflow-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
```
Then, declare your Web Component and encapsulate it in a React component.
@ -115,7 +115,7 @@ Finally, you can place the component anywhere in your code to display the Chat W
To use it in Angular, first add this _`<script>`_ tag into the Angular _index.html_ file, inside the _`<body>`_ tag.
```html
<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/langflow-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
```
When you use a custom web component in an Angular template, the Angular compiler might show a warning when it doesn't recognize the custom elements by default. To suppress this warning, add _`CUSTOM_ELEMENTS_SCHEMA`_ to the module's _`@NgModule.schemas`_.
@ -185,7 +185,7 @@ Use the widget API to customize your Chat Widget:
</Admonition>
| Prop | Type | Required | Description |
| --------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| --------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| bot_message_style | JSON | No | Applies custom formatting to bot messages. |
| chat_input_field | String | Yes | Defines the type of the input field for chat messages. |
| chat_inputs | JSON | Yes | Determines the chat input elements and their respective values. |
@ -208,4 +208,4 @@ Use the widget API to customize your Chat Widget:
| tweaks | JSON | No | Applies additional custom adjustments for the associated flow. |
| user_message_style | JSON | No | Determines the formatting for user messages in the chat window. |
| width | Number | No | Sets the width of the chat window in pixels. |
| window_title | String | No | Sets the title displayed in the chat window's header or title bar. |
| window_title | String | No | Sets the title displayed in the chat window's header or title bar. | import ThemedImage from "@theme/ThemedImage"; |

View file

@ -131,7 +131,7 @@ class MyComponent(CustomComponent):
---
The [Return Type Annotation](https://docs.python.org/3/library/typing.html) of the _`build`_ method defines the component type (e.g., Chain, BaseLLM, or basic Python types). Check out all supported types in the [component reference](../components/custom).
The [Return Type Annotation](https://docs.python.org/3/library/typing.html) of the _`build`_ method defines the component type (e.g., Chain, BaseLanguageModel, or basic Python types). Check out all supported types in the [component reference](../components/custom).
```python
from langflow.custom import CustomComponent
@ -366,7 +366,7 @@ For advanced customization, Langflow offers the option to create and load custom
### Folder Structure
Create a folder that follows the same structural conventions as the [config.yaml](https://github.com/logspace-ai/langflow/blob/dev/src/backend/base/langflow/config.yaml) file. Inside this main directory, use a `custom_components` subdirectory for your custom components.
Create a folder that follows the same structural conventions as the [config.yaml](https://github.com/langflow-ai/langflow/blob/dev/src/backend/base/langflow/config.yaml) file. Inside this main directory, use a `custom_components` subdirectory for your custom components.
Inside `custom_components`, you can create a Python file for each component. Similarly, any custom agents should be housed in an `agents` subdirectory.
@ -406,4 +406,5 @@ Langflow will attempt to load all of the components found in the specified direc
Once your custom components have been loaded successfully, they will appear in Langflow's sidebar. From there, you can add them to your Langflow canvas for use. However, please note that components with errors will not be available for addition to the canvas. Always ensure your code is error-free before attempting to load components.
Remember, creating custom components allows you to extend the functionality of Langflow to better suit your unique needs. Happy coding!
Remember, creating custom components allows you to extend the functionality of Langflow to better suit your unique needs. Happy coding!import ZoomableImage from "/src/theme/ZoomableImage.js";
import Admonition from "@theme/Admonition";

View file

@ -9,8 +9,6 @@ Langflow is an easy way to build from simple to complex AI applications. It is a
{" "}
{" "}
<ZoomableImage
alt="Docusaurus themed image"
sources={{

View file

@ -3,6 +3,15 @@ import Admonition from "@theme/Admonition";
# Global Variables
## TLDR;
- Global Variables are reusable variables that can be accessed from any Text field in your project.
- To create a Global Variable, click on the 🌐 button in a Text field and then **+ Add New Variable**.
- Define the **Name**, **Type**, and **Value** of the variable.
- Click on **Save Variable** to create the variable.
- All Credential Global Variables are encrypted and cannot be accessed by anyone but you.
- Set _`LANGFLOW_STORE_ENVIRONMENT_VARIABLES`_ to _`true`_ in your `.env` file to add all variables in _`LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT`_ to your user's Global Variables.
Global Variables are a really useful feature of Langflow.
They allow you to define reusable variables that can be accessed from any Text field in your project.
@ -48,7 +57,8 @@ The **Value** is the value that the variable will have.
{/* say that all variables are encrypted */}
<Admonition type="warning">
All Global Variables are encrypted and cannot be accessed by anyone but you.
All Credential Global Variables are encrypted and cannot be accessed by anyone
but you.
</Admonition>
<ZoomableImage
@ -63,3 +73,42 @@ The **Value** is the value that the variable will have.
After you have defined your variable, click on **Save Variable** and your variable will be created.
After that, once you click on the 🌐 button in a Text field, you will see your new variable in the dropdown.
## Environment Variables
If you set _`LANGFLOW_STORE_ENVIRONMENT_VARIABLES`_ to _`true`_ (which is the default value) in your `.env` file, all variables in _`LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT`_ will be added to your user's Global Variables.
All of these variables can be used in your project as any other Global Variable.
<Admonition type="tip">
You can set _`LANGFLOW_STORE_ENVIRONMENT_VARIABLES`_ to _`false`_ in your
`.env` file to prevent this behavior.
</Admonition>
You can also set _`LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT`_ to a list of variables that you want to get from the environment.
The default list at the moment is:
- ANTHROPIC_API_KEY
- ASTRA_DB_API_ENDPOINT
- ASTRA_DB_APPLICATION_TOKEN
- AZURE_OPENAI_API_KEY
- AZURE_OPENAI_API_DEPLOYMENT_NAME
- AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME
- AZURE_OPENAI_API_INSTANCE_NAME
- AZURE_OPENAI_API_VERSION
- COHERE_API_KEY
- GOOGLE_API_KEY
- HUGGINGFACEHUB_API_TOKEN
- OPENAI_API_KEY
- SEARCHAPI_API_KEY
- SERPAPI_API_KEY
- VECTARA_CUSTOMER_ID
- VECTARA_CORPUS_ID
- VECTARA_API_KEY
<Admonition type="tip">
Set _`LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT`_ as a comma-separated list
of variables (e.g. _`"VARIABLE1, VARIABLE2"`_) or as a JSON-encoded string
(e.g. _`'["VARIABLE1", "VARIABLE2"]'`_).
</Admonition>

View file

@ -61,7 +61,7 @@ We wanted to create start projects that would help you learn about new features
For now, we have:
- **[Basic Prompting (Hello, world!)](/guides/basic-prompting)**: A simple flow that shows you how to use the Prompt Component and how to talk like a pirate.
- **[Basic Prompting (Hello, World)](/guides/basic-prompting)**: A simple flow that shows you how to use the Prompt Component and how to talk like a pirate.
- **[Vector Store RAG](/guides/rag-with-astradb)**: A flow that shows you how to ingest data into a Vector Store and then use it to run a RAG application.
- **[Memory Chatbot](/guides/memory-chatbot)**: This one shows you how to create a simple chatbot that can remember things about the user.
- **[Document QA](/guides/document-qa)**: This flow shows you how to build a simple flow that helps you get answers about a document.

View file

@ -14,7 +14,7 @@ import Admonition from "@theme/Admonition";
Langflow 1.0 is a significant update that brings many exciting changes and improvements to the platform.
This guide will walk you through the key improvements and help you migrate your existing projects to the new version.
If you have any questions or need assistance during the migration process, please don't hesitate to reach out to in our [Discord](https://discord.gg/wZSWQaukgJ) or [GitHub](https://github.com/logspace-ai/langflow/issues) community.
If you have any questions or need assistance during the migration process, please don't hesitate to reach out to in our [Discord](https://discord.gg/wZSWQaukgJ) or [GitHub](https://github.com/langflow-ai/langflow/issues) community.
We have a special channel in our Discord server dedicated to Langflow 1.0 migration, where you can ask questions, share your experiences, and get help from the community.

View file

@ -44,7 +44,7 @@ module.exports = {
// sidebarPath: 'sidebars.js',
},
gtag: {
trackingID: 'G-XHC7G628ZP',
trackingID: "G-XHC7G628ZP",
anonymizeIP: true,
},
theme: {
@ -87,7 +87,7 @@ module.exports = {
// right
{
position: "right",
href: "https://github.com/logspace-ai/langflow",
href: "https://github.com/langflow-ai/langflow",
position: "right",
className: "header-github-link",
target: "_blank",
@ -124,7 +124,7 @@ module.exports = {
},
announcementBar: {
content:
'⭐️ If you like ⛓Langflow, star it on <a target="_blank" rel="noopener noreferrer" href="https://github.com/logspace-ai/langflow">GitHub</a>! ⭐️',
'⭐️ If you like ⛓Langflow, star it on <a target="_blank" rel="noopener noreferrer" href="https://github.com/langflow-ai/langflow">GitHub</a>! ⭐️',
backgroundColor: "#E8EBF1", //Mustard Yellow #D19900 #D4B20B - Salmon #E9967A
textColor: "#1C1E21",
isCloseable: false,

View file

@ -66,7 +66,7 @@ module.exports = {
},
{
type: "category",
label: "Step-by-Step Guides",
label: "Extended Components",
collapsed: false,
items: ["guides/langfuse_integration"],
},

812
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "langflow"
version = "1.0.0a9"
version = "1.0.0a18"
description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"]
maintainers = [
@ -12,7 +12,7 @@ maintainers = [
"Otávio Anovazzi <otavio2204@gmail.com>",
"Rodrigo Nader <rodrigo@logspace.ai>",
]
repository = "https://github.com/logspace-ai/langflow"
repository = "https://github.com/langflow-ai/langflow"
license = "MIT"
readme = "README.md"
keywords = ["nlp", "langchain", "openai", "gpt", "gui"]
@ -85,10 +85,10 @@ langchain-openai = "^0.1.1"
[tool.poetry.group.dev.dependencies]
types-redis = "^4.6.0.5"
ipykernel = "^6.29.0"
mypy = "^1.8.0"
ruff = "^0.2.1"
mypy = "^1.9.0"
ruff = "^0.3.5"
httpx = "*"
pytest = "^8.0.0"
pytest = "^8.1.0"
types-requests = "^2.31.0"
requests = "^2.31.0"
pytest-cov = "^4.1.0"
@ -106,11 +106,23 @@ pytest-sugar = "^1.0.0"
respx = "^0.21.1"
pytest-instafail = "^0.5.0"
pytest-asyncio = "^0.23.0"
pytest-profiling = "^1.7.0"
[tool.poetry.extras]
deploy = ["celery", "redis", "flower"]
local = ["llama-cpp-python", "sentence-transformers", "ctransformers"]
all = ["deploy", "local"]
[tool.poetry.group.spelling]
optional = true
[tool.poetry.group.spelling.dependencies]
codespell = "^2.2.6"
[tool.codespell]
skip = '.git,*.pdf,*.svg,*.pdf,*.yaml,*.ipynb,poetry.lock,*.min.js,*.css,package-lock.json,*.trig'
# Ignore latin etc
ignore-regex = '.*(Stati Uniti|Tense=Pres).*'
[tool.pytest.ini_options]

View file

@ -4,7 +4,7 @@ services:
name: langflow
runtime: docker
dockerfilePath: ./Dockerfile
repo: https://github.com/logspace-ai/langflow
repo: https://github.com/langflow-ai/langflow
branch: main
healthCheckPath: /health
autoDeploy: false

View file

@ -3,6 +3,7 @@
**想定時間**: 30 分
## 説明
Langflow on AWS では、 [AWS Cloud Development Kit](https://aws.amazon.com/cdk/?nc2=type_a) (CDK) を用いて Langflow を AWS 上にデプロイする方法を学べます。
このチュートリアルは、AWS アカウントと AWS に関する基本的な知識を有していることを前提としています。
@ -10,44 +11,47 @@ Langflow on AWS では、 [AWS Cloud Development Kit](https://aws.amazon.com/cdk
![langflow-archi](./img/langflow-archi.png)
AWS CDK によって Langflow のアプリケーションをデプロイします。アプリケーションは [Amazon CloudFront](https://aws.amazon.com/cloudfront/?nc1=h_ls) を介して配信されます。CloudFront は 2 つのオリジンを有しています。1 つ目は静的な Web サイトを配信するための [Amazon Simple Storage Service](https://aws.amazon.com/s3/?nc1=h_ls) (S3)、2 つ目は バックエンドと通信するための [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/?nc1=h_ls) (ALB) です。ALB の背後には FastAPI が動作する [AWS Fargate](https://aws.amazon.com/fargate/?nc2=type_a) 、データベースの [Amazon Aurora](https://aws.amazon.com/rds/aurora/?nc2=type_a) が作成されます。
Fargate は [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/?nc1=h_ls) (ECR) に保存された Docker イメージを使用します。
Auroraのシークレットは [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/?nc2=type_a) によって管理されます。
Aurora のシークレットは [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/?nc2=type_a) によって管理されます。
# 環境構築とデプロイ方法
1. [AWS CloudShell](https://us-east-1.console.aws.amazon.com/cloudshell/home?region=us-east-1)を開きます。
1. 以下のコマンドを実行します。
```shell
git clone https://github.com/aws-samples/cloud9-setup-for-prototyping
cd cloud9-setup-for-prototyping
./bin/bootstrap
```
```shell
git clone https://github.com/aws-samples/cloud9-setup-for-prototyping
cd cloud9-setup-for-prototyping
./bin/bootstrap
```
1. `Done!` と表示されたら [AWS Cloud9](https://us-east-1.console.aws.amazon.com/cloud9control/home?region=us-east-1#/) から `cloud9-for-prototyping` を開きます。
![make-cloud9](./img/langflow-cloud9.png)
![make-cloud9](./img/langflow-cloud9.png)
1. 以下のコマンドを実行します。
```shell
git clone https://github.com/logspace-ai/langflow.git
cd langflow/scripts/aws
cp .env.example .env # 環境設定を変える場合はこのファイル(.env)を編集してください。
npm ci
cdk bootstrap
cdk deploy
```
```shell
git clone https://github.com/langflow-ai/langflow.git
cd langflow/scripts/aws
cp .env.example .env # 環境設定を変える場合はこのファイル(.env)を編集してください。
npm ci
cdk bootstrap
cdk deploy
```
1. 表示される URL にアクセスします。
```shell
Outputs:
LangflowAppStack.frontendURLXXXXXX = https://XXXXXXXXXXX.cloudfront.net
```
```shell
Outputs:
LangflowAppStack.frontendURLXXXXXX = https://XXXXXXXXXXX.cloudfront.net
```
1. サインイン画面でユーザー名とパスワードを入力します。`.env`ファイルでユーザー名とパスワードを設定していない場合、ユーザー名は`admin`、パスワードは`123456`で設定されます。
![make-cloud9](./img/langflow-signin.png)
![make-cloud9](./img/langflow-signin.png)
# 環境の削除
1. `Cloud9` で以下のコマンドを実行します。
```shell
bash delete-resources.sh
```
1. `Cloud9` で以下のコマンドを実行します。
```shell
bash delete-resources.sh
```
1. [AWS CloudFormation](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/getting-started)を開き、`aws-cloud9-cloud9-for-prototyping-XXXX` を選択して削除します。
![delete-cfn](./img/langflow-cfn.png)
![delete-cfn](./img/langflow-cfn.png)

View file

@ -10,7 +10,7 @@ This tutorial assumes you have an AWS account and basic knowledge of AWS.
The architecture of the application to be created:
![langflow-archi](./img/langflow-archi.png)
Langflow is deployed using AWS CDK. The application is distributed via [Amazon CloudFront](https://aws.amazon.com/cloudfront/?nc1=h_ls), which has two origins: the first is [Amazon Simple Storage Service](https://aws.amazon.com/s3/?nc1=h_ls) (S3) for serving a static website, and the second is an [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/?nc1=h_ls) (ALB) for communicating with the backend. [AWS Fargate](https://aws.amazon.com/fargate/?nc2=type_a), where FastAPI runs and [Amazon Aurora](https://aws.amazon.com/rds/aurora/?nc2=type_a), the database, are created behind the ALB.
Fargate uses a Docker image stored in [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/?nc1=h_ls) (ECR).
Fargate uses a Docker image stored in [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/?nc1=h_ls) (ECR).
Aurora's secret is managed by [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/?nc2=type_a).
# How to set up your environment and deploy langflow
@ -26,14 +26,14 @@ Aurora's secret is managed by [AWS Secrets Manager](https://aws.amazon.com/secre
1. When you see `Done!` in Cloudshell, open `c9-for-langflow` from [AWS Cloud9](https://us-east-1.console.aws.amazon.com/cloud9control/home?region=us-east-1#/).
![make-cloud9](./img/langflow-cloud9-en.png)
1. Run the following command in the Cloud9 terminal.
```shell
git clone https://github.com/logspace-ai/langflow.git
cd langflow/scripts/aws
cp .env.example .env # Edit this file if you need environment settings
npm ci
cdk bootstrap
cdk deploy
```
```shell
git clone https://github.com/langflow-ai/langflow.git
cd langflow/scripts/aws
cp .env.example .env # Edit this file if you need environment settings
npm ci
cdk bootstrap
cdk deploy
```
1. Access the URL displayed.
```shell
Outputs:
@ -50,4 +50,4 @@ Aurora's secret is managed by [AWS Secrets Manager](https://aws.amazon.com/secre
```
1. Open [AWS CloudFormation](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/getting-started), select `aws-cloud9-c9-for-langflow-XXXX` and delete it.
![delete-cfn](./img/langflow-cfn.png)
s
s

View file

@ -9,11 +9,6 @@ import click
import httpx
import typer
from dotenv import load_dotenv
from langflow.main import setup_app
from langflow.services.database.utils import session_getter
from langflow.services.deps import get_db_service, get_settings_service
from langflow.services.utils import initialize_services, initialize_settings_service
from langflow.utils.logger import configure, logger
from multiprocess import Process, cpu_count # type: ignore
from packaging import version as pkg_version
from rich import box
@ -22,6 +17,13 @@ from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from langflow.main import setup_app
from langflow.services.database.utils import session_getter
from langflow.services.deps import get_db_service
from langflow.services.utils import initialize_services
from langflow.utils.logger import configure, logger
from langflow.utils.util import update_settings
console = Console()
app = typer.Typer(no_args_is_help=True)
@ -67,44 +69,10 @@ def set_var_for_macos_issue():
logger.debug("Set OBJC_DISABLE_INITIALIZE_FORK_SAFETY to YES to avoid error")
def update_settings(
config: str,
cache: Optional[str] = None,
dev: bool = False,
remove_api_keys: bool = False,
components_path: Optional[Path] = None,
store: bool = True,
):
"""Update the settings from a config file."""
# Check for database_url in the environment variables
initialize_settings_service()
settings_service = get_settings_service()
if config:
logger.debug(f"Loading settings from {config}")
settings_service.settings.update_from_yaml(config, dev=dev)
if remove_api_keys:
logger.debug(f"Setting remove_api_keys to {remove_api_keys}")
settings_service.settings.update_settings(REMOVE_API_KEYS=remove_api_keys)
if cache:
logger.debug(f"Setting cache to {cache}")
settings_service.settings.update_settings(CACHE=cache)
if components_path:
logger.debug(f"Adding component path {components_path}")
settings_service.settings.update_settings(COMPONENTS_PATH=components_path)
if not store:
logger.debug("Setting store to False")
settings_service.settings.update_settings(STORE=False)
@app.command()
def run(
host: str = typer.Option(
"127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"
),
workers: int = typer.Option(
1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"
),
host: str = typer.Option("127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"),
workers: int = typer.Option(1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"),
timeout: int = typer.Option(300, help="Worker timeout in seconds."),
port: int = typer.Option(7860, help="Port to listen on.", envvar="LANGFLOW_PORT"),
components_path: Optional[Path] = typer.Option(
@ -112,19 +80,11 @@ def run(
help="Path to the directory containing custom components.",
envvar="LANGFLOW_COMPONENTS_PATH",
),
config: str = typer.Option(
Path(__file__).parent / "config.yaml", help="Path to the configuration file."
),
config: str = typer.Option(Path(__file__).parent / "config.yaml", help="Path to the configuration file."),
# .env file param
env_file: Path = typer.Option(
None, help="Path to the .env file containing environment variables."
),
log_level: str = typer.Option(
"critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
),
log_file: Path = typer.Option(
"logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"
),
env_file: Path = typer.Option(None, help="Path to the .env file containing environment variables."),
log_level: str = typer.Option("critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"),
log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"),
cache: Optional[str] = typer.Option(
envvar="LANGFLOW_LANGCHAIN_CACHE",
help="Type of cache to use. (InMemoryCache, SQLiteCache)",
@ -192,17 +152,21 @@ def run(
# Define an env variable to know if we are just testing the server
if "pytest" in sys.modules:
return
if platform.system() in ["Windows"]:
# Run using uvicorn on MacOS and Windows
# Windows doesn't support gunicorn
# MacOS requires an env variable to be set to use gunicorn
run_on_windows(host, port, log_level, options, app)
else:
# Run using gunicorn on Linux
run_on_mac_or_linux(host, port, log_level, options, app, open_browser)
if open_browser:
click.launch(f"http://{host}:{port}")
try:
if platform.system() in ["Windows"]:
# Run using uvicorn on MacOS and Windows
# Windows doesn't support gunicorn
# MacOS requires an env variable to be set to use gunicorn
process = run_on_windows(host, port, log_level, options, app)
else:
# Run using gunicorn on Linux
process = run_on_mac_or_linux(host, port, log_level, options, app)
if open_browser:
click.launch(f"http://{host}:{port}")
if process:
process.join()
except KeyboardInterrupt:
pass
def wait_for_server_ready(host, port):
@ -218,13 +182,12 @@ def wait_for_server_ready(host, port):
def run_on_mac_or_linux(host, port, log_level, options, app):
webapp_process = Process(
target=run_langflow, args=(host, port, log_level, options, app)
)
webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app))
webapp_process.start()
wait_for_server_ready(host, port)
print_banner(host, port)
return webapp_process
def run_on_windows(host, port, log_level, options, app):
@ -233,6 +196,7 @@ def run_on_windows(host, port, log_level, options, app):
"""
print_banner(host, port)
run_langflow(host, port, log_level, options, app)
return None
def is_port_in_use(port, host="localhost"):
@ -316,9 +280,7 @@ def build_new_version_notice(current_version: str, package_name: str):
f"A new pre-release version of {package_name} is available: {latest_version}",
)
else:
latest_version = httpx.get(f"https://pypi.org/pypi/{package_name}/json").json()[
"info"
]["version"]
latest_version = httpx.get(f"https://pypi.org/pypi/{package_name}/json").json()["info"]["version"]
if not version_is_prerelease(latest_version):
return (
False,
@ -331,7 +293,7 @@ def is_prerelease(version: str) -> bool:
return "a" in version or "b" in version or "rc" in version
def fetch_latest_version(package_name: str, include_prerelease: bool) -> str:
def fetch_latest_version(package_name: str, include_prerelease: bool) -> Optional[str]:
response = httpx.get(f"https://pypi.org/pypi/{package_name}/json")
versions = response.json()["releases"].keys()
valid_versions = [v for v in versions if include_prerelease or not is_prerelease(v)]
@ -342,9 +304,7 @@ def fetch_latest_version(package_name: str, include_prerelease: bool) -> str:
def build_version_notice(current_version: str, package_name: str) -> str:
latest_version = fetch_latest_version(package_name, is_prerelease(current_version))
if latest_version and pkg_version.parse(current_version) < pkg_version.parse(
latest_version
):
if latest_version and pkg_version.parse(current_version) < pkg_version.parse(latest_version):
release_type = "pre-release" if is_prerelease(latest_version) else "version"
return f"A new {release_type} of {package_name} is available: {latest_version}"
return ""
@ -375,7 +335,7 @@ def print_banner(host: str, port: int):
package_name = ""
try:
from langflow.version import __version__ as langflow_version
from langflow.version import __version__ as langflow_version # type: ignore
is_pre_release |= is_prerelease(langflow_version) # Update pre-release status
notice = build_version_notice(langflow_version, "langflow")
@ -393,9 +353,7 @@ def print_banner(host: str, port: int):
from importlib import metadata
langflow_base_version = metadata.version("langflow-base")
is_pre_release |= is_prerelease(
langflow_base_version
) # Update pre-release status
is_pre_release |= is_prerelease(langflow_base_version) # Update pre-release status
notice = build_version_notice(langflow_base_version, "langflow-base")
notice = stylize_text(notice, "langflow-base", is_pre_release)
if notice:
@ -414,12 +372,10 @@ def print_banner(host: str, port: int):
notices.append(f"Run '{pip_command}' to update.")
styled_notices = [f"[bold]{notice}[/bold]" for notice in notices if notice]
styled_package_name = stylize_text(
package_name, package_name, any("pre-release" in notice for notice in notices)
)
styled_package_name = stylize_text(package_name, package_name, any("pre-release" in notice for notice in notices))
title = f"[bold]Welcome to :chains: {styled_package_name}[/bold]\n"
info_text = "Collaborate, and contribute at our [bold][link=https://github.com/logspace-ai/langflow]GitHub Repo[/link][/bold] :rocket:"
info_text = "Collaborate, and contribute at our [bold][link=https://github.com/langflow-ai/langflow]GitHub Repo[/link][/bold] :rocket:"
access_link = f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]"
panel_content = "\n\n".join([title, *styled_notices, info_text, access_link])
@ -459,12 +415,8 @@ def run_langflow(host, port, log_level, options, app):
@app.command()
def superuser(
username: str = typer.Option(..., prompt=True, help="Username for the superuser."),
password: str = typer.Option(
..., prompt=True, hide_input=True, help="Password for the superuser."
),
log_level: str = typer.Option(
"error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"
),
password: str = typer.Option(..., prompt=True, hide_input=True, help="Password for the superuser."),
log_level: str = typer.Option("error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"),
):
"""
Create a superuser.
@ -491,11 +443,23 @@ def superuser(
@app.command()
def migration(test: bool = typer.Option(True, help="Run migrations in test mode.")):
def migration(
test: bool = typer.Option(True, help="Run migrations in test mode."),
fix: bool = typer.Option(
False,
help="Fix migrations. This is a destructive operation, and should only be used if you know what you are doing.",
),
):
"""
Run or test migrations.
"""
initialize_services()
if fix:
if not typer.confirm(
"This will delete all data necessary to fix migrations. Are you sure you want to continue?"
):
raise typer.Abort()
initialize_services(fix_migration=fix)
db_service = get_db_service()
if not test:
db_service.run_migrations()

View file

@ -0,0 +1,99 @@
"""Change datetime type
Revision ID: 79e675cb6752
Revises: e3bc869fa272
Create Date: 2024-04-11 19:23:10.697335
"""
from calendar import c
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic.
revision: str = "79e675cb6752"
down_revision: Union[str, None] = "e3bc869fa272"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
table_names = inspector.get_table_names()
# ### commands auto generated by Alembic - please adjust! ###
if "apikey" in table_names:
columns = inspector.get_columns("apikey")
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
if created_at_column is not None and created_at_column["type"] == postgresql.TIMESTAMP():
with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.alter_column(
"created_at",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
)
if "variable" in table_names:
columns = inspector.get_columns("variable")
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
updated_at_column = next((column for column in columns if column["name"] == "updated_at"), None)
with op.batch_alter_table("variable", schema=None) as batch_op:
if created_at_column is not None and created_at_column["type"] == postgresql.TIMESTAMP():
batch_op.alter_column(
"created_at",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=True,
)
if updated_at_column is not None and updated_at_column["type"] == postgresql.TIMESTAMP():
batch_op.alter_column(
"updated_at",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=True,
)
# ### end Alembic commands ###
def downgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
table_names = inspector.get_table_names()
# ### commands auto generated by Alembic - please adjust! ###
if "variable" in table_names:
columns = inspector.get_columns("variable")
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
updated_at_column = next((column for column in columns if column["name"] == "updated_at"), None)
with op.batch_alter_table("variable", schema=None) as batch_op:
if updated_at_column is not None and updated_at_column["type"] == sa.DateTime(timezone=True):
batch_op.alter_column(
"updated_at",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=True,
)
if created_at_column is not None and created_at_column["type"] == sa.DateTime(timezone=True):
batch_op.alter_column(
"created_at",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=True,
)
if "apikey" in table_names:
columns = inspector.get_columns("apikey")
created_at_column = next((column for column in columns if column["name"] == "created_at"), None)
if created_at_column is not None and created_at_column["type"] == sa.DateTime(timezone=True):
with op.batch_alter_table("apikey", schema=None) as batch_op:
batch_op.alter_column(
"created_at",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
)
# ### end Alembic commands ###

View file

@ -0,0 +1,62 @@
"""Fix nullable
Revision ID: e3bc869fa272
Revises: 1a110b568907
Create Date: 2024-04-10 19:17:22.820455
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic.
revision: str = "e3bc869fa272"
down_revision: Union[str, None] = "1a110b568907"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
table_names = inspector.get_table_names()
# ### commands auto generated by Alembic - please adjust! ###
if "variable" not in table_names:
return
column_names = [column["name"] for column in inspector.get_columns("variable")]
with op.batch_alter_table("variable", schema=None) as batch_op:
if "created_at" in column_names:
batch_op.alter_column(
"created_at",
existing_type=sa.TIMESTAMP(timezone=True),
nullable=True,
# existing_server_default expects str | bool | Identity | Computed | None
# sa.text("now()") is not a valid value for existing_server_default
existing_server_default=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
conn = op.get_bind()
inspector = Inspector.from_engine(conn) # type: ignore
table_names = inspector.get_table_names()
# ### commands auto generated by Alembic - please adjust! ###
if "variable" not in table_names:
return
with op.batch_alter_table("variable", schema=None) as batch_op:
if "created_at" in inspector.get_columns("variable"):
batch_op.alter_column(
"created_at",
existing_type=sa.TIMESTAMP(timezone=True),
nullable=False,
existing_server_default=False,
)
# ### end Alembic commands ###

View file

@ -14,7 +14,6 @@ from langflow.api.v1.schemas import (
RunResponse,
SimplifiedAPIRequest,
TaskStatusResponse,
Tweaks,
UpdateCustomComponentRequest,
UploadFileResponse,
)
@ -24,6 +23,7 @@ from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.interface.custom.utils import build_custom_component_template
from langflow.processing.process import process_tweaks, run_graph_internal
from langflow.schema.graph import Tweaks
from langflow.services.auth.utils import api_key_security, get_current_active_user
from langflow.services.cache.utils import save_uploaded_file
from langflow.services.database.models.flow import Flow
@ -373,7 +373,7 @@ async def create_upload_file(
@router.get("/version")
def get_version():
try:
from langflow.version import __version__
from langflow.version import __version__ # type: ignore
version = __version__
package = "Langflow"

View file

@ -1,7 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlmodel import Session
from langflow.api.v1.schemas import Token
from langflow.services.auth.utils import (
authenticate_user,
@ -9,8 +7,14 @@ from langflow.services.auth.utils import (
create_user_longterm_token,
create_user_tokens,
)
from langflow.services.deps import get_session, get_settings_service
from langflow.services.deps import (
get_session,
get_settings_service,
get_variable_service,
)
from langflow.services.settings.manager import SettingsService
from langflow.services.variable.service import VariableService
from sqlmodel import Session
router = APIRouter(tags=["Login"])
@ -22,6 +26,7 @@ async def login_to_get_access_token(
db: Session = Depends(get_session),
# _: Session = Depends(get_current_active_user)
settings_service=Depends(get_settings_service),
variable_service: VariableService = Depends(get_variable_service),
):
auth_settings = settings_service.auth_settings
try:
@ -52,6 +57,7 @@ async def login_to_get_access_token(
secure=auth_settings.ACCESS_SECURE,
expires=auth_settings.ACCESS_TOKEN_EXPIRE_SECONDS,
)
variable_service.initialize_user_variables(user.id, db)
return tokens
else:
raise HTTPException(
@ -66,10 +72,11 @@ async def auto_login(
response: Response,
db: Session = Depends(get_session),
settings_service=Depends(get_settings_service),
variable_service: VariableService = Depends(get_variable_service),
):
auth_settings = settings_service.auth_settings
if settings_service.auth_settings.AUTO_LOGIN:
tokens = create_user_longterm_token(db)
user_id, tokens = create_user_longterm_token(db)
response.set_cookie(
"access_token_lf",
tokens["access_token"],
@ -78,6 +85,7 @@ async def auto_login(
secure=auth_settings.ACCESS_SECURE,
expires=None, # Set to None to make it a session cookie
)
variable_service.initialize_user_variables(user_id, db)
return tokens
raise HTTPException(
@ -91,7 +99,9 @@ async def auto_login(
@router.post("/refresh")
async def refresh_token(
request: Request, response: Response, settings_service: "SettingsService" = Depends(get_settings_service)
request: Request,
response: Response,
settings_service: "SettingsService" = Depends(get_settings_service),
):
auth_settings = settings_service.auth_settings
@ -129,3 +139,4 @@ async def logout(response: Response):
response.delete_cookie("refresh_token_lf")
response.delete_cookie("access_token_lf")
return {"message": "Logout successful"}
return {"message": "Logout successful"}

View file

@ -4,10 +4,11 @@ from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_serializer
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_serializer
from langflow.graph.schema import RunOutputs
from langflow.schema import dotdict
from langflow.schema.graph import Tweaks
from langflow.schema.schema import InputType, OutputType
from langflow.services.database.models.api_key.model import ApiKeyRead
from langflow.services.database.models.base import orjson_dumps
@ -283,36 +284,6 @@ class InputValueRequest(BaseModel):
)
class Tweaks(RootModel):
root: dict[str, Union[str, dict[str, str]]] = Field(
description="A dictionary of tweaks to adjust the flow's execution. Allows customizing flow behavior dynamically. All tweaks are overridden by the input values.",
)
model_config = {
"json_schema_extra": {
"examples": [
{
"parameter_name": "value",
"Component Name": {"parameter_name": "value"},
"component_id": {"parameter_name": "value"},
}
]
}
}
# This should behave like a dict
def __getitem__(self, key):
return self.root[key]
def __setitem__(self, key, value):
self.root[key] = value
def __delitem__(self, key):
del self.root[key]
def items(self):
return self.root.items()
class SimplifiedAPIRequest(BaseModel):
input_value: Optional[str] = Field(default="", description="The input value")
input_type: Optional[InputType] = Field(default="chat", description="The input type")

View file

@ -4,7 +4,7 @@ from fastapi import APIRouter, HTTPException
from loguru import logger
from langflow.api.v1.base import Code, CodeValidationResponse, PromptValidationResponse, ValidatePromptRequest
from langflow.base.prompts.utils import (
from langflow.base.prompts.api_utils import (
add_new_variables_to_template,
get_old_custom_fields,
remove_old_variables_from_template,

View file

@ -24,4 +24,5 @@ FIELD_FORMAT_ATTRIBUTES = [
"real_time_refresh",
"refresh_button",
"refresh_button_text",
"options",
]

View file

@ -10,7 +10,26 @@ from langflow.schema.schema import Record
# Types of files that can be read simply by file.read()
# and have 100% to be completely readable
TEXT_FILE_TYPES = ["txt", "md", "mdx", "csv", "json", "yaml", "yml", "xml", "html", "htm", "pdf", "docx"]
TEXT_FILE_TYPES = [
"txt",
"md",
"mdx",
"csv",
"json",
"yaml",
"yml",
"xml",
"html",
"htm",
"pdf",
"docx",
"py",
"sh",
"sql",
"js",
"ts",
"tsx",
]
def is_hidden(path: Path) -> bool:

View file

@ -79,7 +79,7 @@ class ChatComponent(CustomComponent):
records = add_messages([record])
return records[0]
def build(
def build_with_record(
self,
sender: Optional[str] = "User",
sender_name: Optional[str] = "User",
@ -116,3 +116,41 @@ class ChatComponent(CustomComponent):
if session_id and isinstance(result, (Record, str)):
self.store_message(result, session_id, sender, sender_name)
return result
def build_no_record(
self,
sender: Optional[str] = "User",
sender_name: Optional[str] = "User",
input_value: Optional[str] = None,
session_id: Optional[str] = None,
return_record: Optional[bool] = False,
record_template: str = "Text: {text}\nData: {data}",
) -> Union[Text, Record]:
input_value_record: Optional[Record] = None
if return_record:
if isinstance(input_value, Record):
# Update the data of the record
input_value.data["sender"] = sender
input_value.data["sender_name"] = sender_name
input_value.data["session_id"] = session_id
else:
input_value_record = Record(
text=input_value,
data={
"sender": sender,
"sender_name": sender_name,
"session_id": session_id,
},
)
elif isinstance(input_value, Record):
input_value = records_to_text(template=record_template, records=input_value)
if not input_value:
input_value = ""
if return_record and input_value_record:
result: Union[Text, Record] = input_value_record
else:
result = input_value
self.status = result
if session_id and isinstance(result, (Record, str)):
self.store_message(result, session_id, sender, sender_name)
return result

View file

@ -0,0 +1,83 @@
from fastapi import HTTPException
from langchain.prompts import PromptTemplate
from loguru import logger
from langflow.api.v1.base import INVALID_NAMES, check_input_variables
from langflow.interface.utils import extract_input_variables_from_prompt
from langflow.template.field.prompt import DefaultPromptField
def validate_prompt(prompt_template: str, silent_errors: bool = False) -> list[str]:
input_variables = extract_input_variables_from_prompt(prompt_template)
# Check if there are invalid characters in the input_variables
input_variables = check_input_variables(input_variables)
if any(var in INVALID_NAMES for var in input_variables):
raise ValueError(f"Invalid input variables. None of the variables can be named {', '.join(input_variables)}. ")
try:
PromptTemplate(template=prompt_template, input_variables=input_variables)
except Exception as exc:
logger.error(f"Invalid prompt: {exc}")
if not silent_errors:
raise ValueError(f"Invalid prompt: {exc}") from exc
return input_variables
def get_old_custom_fields(custom_fields, name):
try:
if len(custom_fields) == 1 and name == "":
# If there is only one custom field and the name is empty string
# then we are dealing with the first prompt request after the node was created
name = list(custom_fields.keys())[0]
old_custom_fields = custom_fields[name]
if not old_custom_fields:
old_custom_fields = []
old_custom_fields = old_custom_fields.copy()
except KeyError:
old_custom_fields = []
custom_fields[name] = []
return old_custom_fields
def add_new_variables_to_template(input_variables, custom_fields, template, name):
for variable in input_variables:
try:
template_field = DefaultPromptField(name=variable, display_name=variable)
if variable in template:
# Set the new field with the old value
template_field.value = template[variable]["value"]
template[variable] = template_field.to_dict()
# Check if variable is not already in the list before appending
if variable not in custom_fields[name]:
custom_fields[name].append(variable)
except Exception as exc:
logger.exception(exc)
raise HTTPException(status_code=500, detail=str(exc)) from exc
def remove_old_variables_from_template(old_custom_fields, input_variables, custom_fields, template, name):
for variable in old_custom_fields:
if variable not in input_variables:
try:
# Remove the variable from custom_fields associated with the given name
if variable in custom_fields[name]:
custom_fields[name].remove(variable)
# Remove the variable from the template
template.pop(variable, None)
except Exception as exc:
logger.exception(exc)
raise HTTPException(status_code=500, detail=str(exc)) from exc
def update_input_variables_field(input_variables, template):
if "input_variables" in template:
template["input_variables"]["value"] = input_variables

View file

@ -1,12 +1,19 @@
from fastapi import HTTPException
from langchain.prompts import PromptTemplate
from langchain_core.documents import Document
from loguru import logger
from langflow.api.v1.base import INVALID_NAMES, check_input_variables
from langflow.interface.utils import extract_input_variables_from_prompt
from langflow.schema import Record
from langflow.template.field.prompt import DefaultPromptField
def record_to_string(record: Record) -> str:
"""
Convert a record to a string.
Args:
record (Record): The record to convert.
Returns:
str: The record as a string.
"""
return record.get_text()
def dict_values_to_string(d: dict) -> dict:
@ -35,19 +42,6 @@ def dict_values_to_string(d: dict) -> dict:
return d
def record_to_string(record: Record) -> str:
"""
Convert a record to a string.
Args:
record (Record): The record to convert.
Returns:
str: The record as a string.
"""
return record.get_text()
def document_to_string(document: Document) -> str:
"""
Convert a document to a string.
@ -59,79 +53,3 @@ def document_to_string(document: Document) -> str:
str: The document as a string.
"""
return document.page_content
def validate_prompt(prompt_template: str, silent_errors: bool = False) -> list[str]:
input_variables = extract_input_variables_from_prompt(prompt_template)
# Check if there are invalid characters in the input_variables
input_variables = check_input_variables(input_variables)
if any(var in INVALID_NAMES for var in input_variables):
raise ValueError(f"Invalid input variables. None of the variables can be named {', '.join(input_variables)}. ")
try:
PromptTemplate(template=prompt_template, input_variables=input_variables)
except Exception as exc:
logger.error(f"Invalid prompt: {exc}")
if not silent_errors:
raise ValueError(f"Invalid prompt: {exc}") from exc
return input_variables
def get_old_custom_fields(custom_fields, name):
try:
if len(custom_fields) == 1 and name == "":
# If there is only one custom field and the name is empty string
# then we are dealing with the first prompt request after the node was created
name = list(custom_fields.keys())[0]
old_custom_fields = custom_fields[name]
if not old_custom_fields:
old_custom_fields = []
old_custom_fields = old_custom_fields.copy()
except KeyError:
old_custom_fields = []
custom_fields[name] = []
return old_custom_fields
def add_new_variables_to_template(input_variables, custom_fields, template, name):
for variable in input_variables:
try:
template_field = DefaultPromptField(name=variable, display_name=variable)
if variable in template:
# Set the new field with the old value
template_field.value = template[variable]["value"]
template[variable] = template_field.to_dict()
# Check if variable is not already in the list before appending
if variable not in custom_fields[name]:
custom_fields[name].append(variable)
except Exception as exc:
logger.exception(exc)
raise HTTPException(status_code=500, detail=str(exc)) from exc
def remove_old_variables_from_template(old_custom_fields, input_variables, custom_fields, template, name):
for variable in old_custom_fields:
if variable not in input_variables:
try:
# Remove the variable from custom_fields associated with the given name
if variable in custom_fields[name]:
custom_fields[name].remove(variable)
# Remove the variable from the template
template.pop(variable, None)
except Exception as exc:
logger.exception(exc)
raise HTTPException(status_code=500, detail=str(exc)) from exc
def update_input_variables_field(input_variables, template):
if "input_variables" in template:
template["input_variables"]["value"] = input_variables

View file

@ -0,0 +1,23 @@
from langflow.field_typing import Tool
def build_status_from_tool(tool: Tool):
"""
Builds a status string representation of a tool.
Args:
tool (Tool): The tool object to build the status for.
Returns:
str: The status string representation of the tool, including its name, description, and arguments (if any).
"""
description_repr = repr(tool.description).strip("'")
args_str = "\n".join(
[
f"- {arg_name}: {arg_data['description']}"
for arg_name, arg_data in tool.args.items()
if "description" in arg_data
]
)
status = f"Name: {tool.name}\nDescription: {description_repr}"
return status + (f"\nArguments:\n{args_str}" if args_str else "")

View file

@ -4,7 +4,7 @@ from langchain.agents import create_xml_agent
from langchain_core.prompts import PromptTemplate
from langflow.base.agents.agent import LCAgentComponent
from langflow.field_typing import BaseLLM, BaseMemory, Text, Tool
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text, Tool
class XMLAgentComponent(LCAgentComponent):
@ -66,7 +66,7 @@ class XMLAgentComponent(LCAgentComponent):
async def build(
self,
input_value: str,
llm: BaseLLM,
llm: BaseLanguageModel,
tools: List[Tool],
prompt: str,
memory: Optional[BaseMemory] = None,

View file

@ -1,9 +1,10 @@
from typing import Optional
from langchain.chains import LLMChain
from langchain.chains.llm import LLMChain
from langflow.field_typing import BaseLanguageModel, BaseMemory, BasePromptTemplate, Text
from langflow.field_typing import BaseLanguageModel, BaseMemory, Text
from langflow.interface.custom.custom_component import CustomComponent
from langchain_core.prompts import PromptTemplate
class LLMChainComponent(CustomComponent):
@ -19,10 +20,11 @@ class LLMChainComponent(CustomComponent):
def build(
self,
prompt: BasePromptTemplate,
template: Text,
llm: BaseLanguageModel,
memory: Optional[BaseMemory] = None,
) -> Text:
prompt = PromptTemplate.from_template(template)
runnable = LLMChain(prompt=prompt, llm=llm, memory=memory)
result_dict = runnable.invoke({})
output_key = runnable.output_key

View file

@ -3,7 +3,6 @@ import json
from typing import List, Optional
import httpx
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema import Record
@ -47,8 +46,8 @@ class APIRequest(CustomComponent):
client: httpx.AsyncClient,
method: str,
url: str,
headers: Optional[Record] = None,
body: Optional[Record] = None,
headers: Optional[dict] = None,
body: Optional[dict] = None,
timeout: int = 5,
) -> Record:
method = method.upper()

View file

@ -1,6 +1,7 @@
from typing import Any, Dict, List, Optional
from langchain_openai.embeddings.base import OpenAIEmbeddings
from pydantic.v1 import SecretStr
from langflow.field_typing import Embeddings, NestedDict
from langflow.interface.custom.custom_component import CustomComponent
@ -113,6 +114,10 @@ class OpenAIEmbeddingsComponent(CustomComponent):
# This is to avoid errors with Vector Stores (e.g Chroma)
if disallowed_special == ["all"]:
disallowed_special = "all" # type: ignore
if openai_api_key:
api_key = SecretStr(openai_api_key)
else:
api_key = None
return OpenAIEmbeddings(
tiktoken_enabled=tiktoken_enable,
@ -128,7 +133,7 @@ class OpenAIEmbeddingsComponent(CustomComponent):
model=model,
model_kwargs=model_kwargs,
base_url=openai_api_base,
api_key=openai_api_key,
api_key=api_key,
openai_api_type=openai_api_type,
api_version=openai_api_version,
organization=openai_organization,

View file

@ -1,14 +1,14 @@
from typing import Any, List, Optional
from asyncer import syncify
from langchain_core.tools import StructuredTool
from loguru import logger
from langflow.custom import CustomComponent
from langflow.field_typing import Tool
from langflow.graph.graph.base import Graph
from langflow.helpers.flow import build_function_and_schema
from langflow.schema.dotdict import dotdict
from langflow.schema.schema import Record
from loguru import logger
class FlowToolComponent(CustomComponent):
@ -74,6 +74,7 @@ class FlowToolComponent(CustomComponent):
graph = Graph.from_payload(flow_record.data["data"])
dynamic_flow_function, schema = build_function_and_schema(flow_record, graph)
tool = StructuredTool.from_function(
func=syncify(dynamic_flow_function, raise_sync_error=False), # type: ignore
coroutine=dynamic_flow_function,
name=name,
description=description,

View file

@ -61,6 +61,6 @@ class MemoryComponent(CustomComponent):
limit=n_messages,
order=order,
)
messages_str = records_to_text(template=record_template, records=messages)
messages_str = records_to_text(template=record_template or "", records=messages)
self.status = messages_str
return messages_str

View file

@ -1,4 +1,4 @@
from typing import Optional
from typing import Optional, Union
from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter
from langchain_core.documents import Document
@ -51,6 +51,8 @@ class SplitTextComponent(CustomComponent):
chunk_overlap: Optional[int] = 200,
recursive: bool = False,
) -> list[Record]:
if separators is None:
separators = []
separators = [unescape_string(x) for x in separators]
# Make sure chunk_size and chunk_overlap are ints
@ -58,7 +60,7 @@ class SplitTextComponent(CustomComponent):
chunk_size = int(chunk_size)
if isinstance(chunk_overlap, str):
chunk_overlap = int(chunk_overlap)
splitter: Optional[Union[CharacterTextSplitter, RecursiveCharacterTextSplitter]] = None
if recursive:
splitter = RecursiveCharacterTextSplitter(
separators=separators,

View file

@ -28,7 +28,7 @@ class ChatInput(ChatComponent):
session_id: Optional[str] = None,
return_record: Optional[bool] = False,
) -> Union[Text, Record]:
return super().build(
return super().build_no_record(
sender=sender,
sender_name=sender_name,
input_value=input_value,

View file

@ -26,7 +26,7 @@ class TextInput(TextComponent):
def build(
self,
input_value: Optional[str] = "",
input_value: Optional[Text] = "",
record_template: Optional[str] = "",
) -> Text:
return super().build(input_value=input_value, record_template=record_template)

View file

@ -1,6 +1,5 @@
from typing import Optional
from langchain.llms.base import BaseLLM
from langflow.field_typing import BaseLanguageModel
from langchain_community.llms.bedrock import Bedrock
from langflow.interface.custom.custom_component import CustomComponent
@ -46,7 +45,7 @@ class AmazonBedrockComponent(CustomComponent):
endpoint_url: Optional[str] = None,
streaming: bool = False,
cache: Optional[bool] = None,
) -> BaseLLM:
) -> BaseLanguageModel:
try:
output = Bedrock(
credentials_profile_name=credentials_profile_name,

View file

@ -1,14 +1,14 @@
from typing import Optional
from langchain.llms.base import BaseLanguageModel
from langchain_community.chat_models.anthropic import ChatAnthropic
from langchain_anthropic import ChatAnthropic
from pydantic.v1 import SecretStr
from langflow.interface.custom.custom_component import CustomComponent
class AnthropicLLM(CustomComponent):
display_name: str = "AnthropicLLM"
class ChatAntropicSpecsComponent(CustomComponent):
display_name: str = "Anthropic"
description: str = "Anthropic Chat&Completion large language models."
icon = "Anthropic"

View file

@ -1,49 +0,0 @@
from typing import Optional
from langchain_community.llms.anthropic import Anthropic
from pydantic.v1 import SecretStr
from langflow.field_typing import BaseLanguageModel, NestedDict
from langflow.interface.custom.custom_component import CustomComponent
class AnthropicComponent(CustomComponent):
display_name = "Anthropic"
description = "Anthropic large language models."
icon = "Anthropic"
def build_config(self):
return {
"anthropic_api_key": {
"display_name": "Anthropic API Key",
"type": str,
"password": True,
},
"anthropic_api_url": {
"display_name": "Anthropic API URL",
"type": str,
},
"model_kwargs": {
"display_name": "Model Kwargs",
"field_type": "NestedDict",
"advanced": True,
},
"temperature": {
"display_name": "Temperature",
"field_type": "float",
},
}
def build(
self,
anthropic_api_key: str,
anthropic_api_url: str,
model_kwargs: NestedDict = {},
temperature: Optional[float] = None,
) -> BaseLanguageModel:
return Anthropic(
anthropic_api_key=SecretStr(anthropic_api_key),
anthropic_api_url=anthropic_api_url,
model_kwargs=model_kwargs,
temperature=temperature,
)

View file

@ -1,9 +1,9 @@
from typing import Optional
from langchain.llms.base import BaseLLM
from langchain_community.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint
from pydantic.v1 import SecretStr
from langflow.field_typing import BaseLanguageModel
from langflow.interface.custom.custom_component import CustomComponent
@ -79,7 +79,7 @@ class QianfanChatEndpointComponent(CustomComponent):
temperature: Optional[float] = None,
penalty_score: Optional[float] = None,
endpoint: Optional[str] = None,
) -> BaseLLM:
) -> BaseLanguageModel:
try:
output = QianfanChatEndpoint( # type: ignore
model=model,

View file

@ -1,9 +1,9 @@
from typing import Optional
from langchain.llms.baidu_qianfan_endpoint import QianfanLLMEndpoint
from langchain.llms.base import BaseLLM
from langflow.interface.custom.custom_component import CustomComponent
from langflow.field_typing import BaseLanguageModel
class QianfanLLMEndpointComponent(CustomComponent):
@ -78,7 +78,7 @@ class QianfanLLMEndpointComponent(CustomComponent):
temperature: Optional[float] = None,
penalty_score: Optional[float] = None,
endpoint: Optional[str] = None,
) -> BaseLLM:
) -> BaseLanguageModel:
try:
output = QianfanLLMEndpoint( # type: ignore
model=model,

View file

@ -1,67 +1,89 @@
from typing import Callable, Optional, Union
from typing import Optional
from langchain_community.chat_models.anthropic import ChatAnthropic
from langchain_anthropic import ChatAnthropic
from pydantic.v1.types import SecretStr
from langflow.custom import CustomComponent
from langflow.field_typing import BaseLanguageModel
class ChatAnthropicComponent(CustomComponent):
display_name = "ChatAnthropic"
description = "`Anthropic` chat large language models."
documentation = "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
class AnthropicLLM(CustomComponent):
display_name: str = "Anthropic"
description: str = "Generate text using Anthropic Chat&Completion LLMs."
icon = "Anthropic"
field_order = [
"model",
"anthropic_api_key",
"max_tokens",
"temperature",
"anthropic_api_url",
]
def build_config(self):
return {
"model": {
"display_name": "Model Name",
"options": [
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307",
"claude-2.1",
"claude-2.0",
"claude-instant-1.2",
"claude-instant-1",
],
"info": "Name of the model to use.",
"required": True,
"value": "claude-3-opus-20240229",
},
"anthropic_api_key": {
"display_name": "Anthropic API Key",
"field_type": "str",
"required": True,
"password": True,
},
"model_kwargs": {
"display_name": "Model Kwargs",
"field_type": "dict",
"advanced": True,
},
"model_name": {
"display_name": "Model Name",
"field_type": "str",
"advanced": False,
"required": False,
"options": ["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"],
},
"temperature": {
"display_name": "Temperature",
"field_type": "float",
"info": "Your Anthropic API key.",
},
"max_tokens": {
"display_name": "Max Tokens",
"field_type": "int",
"advanced": False,
"required": False,
"advanced": True,
"value": 256,
},
"top_k": {"display_name": "Top K", "field_type": "int", "advanced": True},
"top_p": {"display_name": "Top P", "field_type": "float", "advanced": True},
"temperature": {
"display_name": "Temperature",
"field_type": "float",
"value": 0.1,
},
"anthropic_api_url": {
"display_name": "Anthropic API URL",
"advanced": True,
"info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.",
},
"code": {"show": False},
}
def build(
self,
anthropic_api_key: str,
model_kwargs: dict = {},
model_name: str = "claude-3-opus-20240229",
model: str,
anthropic_api_key: Optional[str] = None,
max_tokens: Optional[int] = None,
temperature: Optional[float] = None,
max_tokens: Optional[int] = 1024,
top_k: Optional[int] = None,
top_p: Optional[float] = None,
) -> Union[BaseLanguageModel, Callable]:
return ChatAnthropic(
anthropic_api_key=SecretStr(anthropic_api_key),
model_kwargs=model_kwargs,
model_name=model_name,
temperature=temperature,
max_tokens=max_tokens, # type: ignore
top_k=top_k,
top_p=top_p,
)
anthropic_api_url: Optional[str] = None,
) -> BaseLanguageModel:
# Set default API endpoint if not provided
if not anthropic_api_url:
anthropic_api_url = "https://api.anthropic.com"
try:
output = ChatAnthropic(
model_name=model,
anthropic_api_key=(SecretStr(anthropic_api_key) if anthropic_api_key else None),
max_tokens_to_sample=max_tokens, # type: ignore
temperature=temperature,
anthropic_api_url=anthropic_api_url,
)
except Exception as e:
raise ValueError("Could not connect to Anthropic API.") from e
return output

View file

@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, Optional, Union
from typing import Any, Dict, Optional
from langchain_community.chat_models.litellm import ChatLiteLLM, ChatLiteLLMException
from langflow.field_typing import BaseLanguageModel

View file

@ -1,9 +1,9 @@
from typing import Optional, Union
from typing import Optional
from langchain.llms import BaseLLM
from langflow.field_typing import BaseLanguageModel
from langchain_community.chat_models.openai import ChatOpenAI
from langflow.field_typing import BaseLanguageModel, NestedDict
from langflow.field_typing import NestedDict
from langflow.interface.custom.custom_component import CustomComponent
@ -68,7 +68,7 @@ class ChatOpenAIComponent(CustomComponent):
openai_api_base: Optional[str] = None,
openai_api_key: Optional[str] = None,
temperature: float = 0.7,
) -> Union[BaseLanguageModel, BaseLLM]:
) -> BaseLanguageModel:
if not openai_api_base:
openai_api_base = "https://api.openai.com/v1"
return ChatOpenAI(

View file

@ -1,6 +1,5 @@
from typing import List, Optional, Union
from typing import List, Optional
from langchain.llms import BaseLLM
from langchain_community.chat_models.vertexai import ChatVertexAI
from langchain_core.messages.base import BaseMessage
@ -74,7 +73,7 @@ class ChatVertexAIComponent(CustomComponent):
top_k: int = 40,
top_p: float = 0.95,
verbose: bool = False,
) -> Union[BaseLanguageModel, BaseLLM]:
) -> BaseLanguageModel:
return ChatVertexAI(
credentials=credentials,
examples=examples,

View file

@ -1,6 +1,6 @@
from typing import Optional
from langchain.llms.base import BaseLLM
from langflow.field_typing import BaseLanguageModel
from langchain.llms.huggingface_endpoint import HuggingFaceEndpoint
from langflow.interface.custom.custom_component import CustomComponent
@ -32,7 +32,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
task: str = "text2text-generation",
huggingfacehub_api_token: Optional[str] = None,
model_kwargs: Optional[dict] = None,
) -> BaseLLM:
) -> BaseLanguageModel:
try:
output = HuggingFaceEndpoint( # type: ignore
endpoint_url=endpoint_url,

View file

@ -1,6 +1,6 @@
from typing import List, Optional
from langchain.llms.base import BaseLLM
from langflow.field_typing import BaseLanguageModel
from langchain_community.llms.ollama import Ollama
from langflow.interface.custom.custom_component import CustomComponent
@ -118,7 +118,7 @@ class OllamaLLM(CustomComponent):
tfs_z: Optional[float] = None,
top_k: Optional[int] = None,
top_p: Optional[int] = None,
) -> BaseLLM:
) -> BaseLanguageModel:
if not base_url:
base_url = "http://localhost:11434"

View file

@ -1,6 +1,6 @@
from typing import Callable, Dict, Optional, Union
from typing import Dict, Optional
from langchain.llms import BaseLLM
from langflow.field_typing import BaseLanguageModel
from langchain_community.llms.vertexai import VertexAI
from langflow.interface.custom.custom_component import CustomComponent
@ -129,7 +129,7 @@ class VertexAIComponent(CustomComponent):
top_p: float = 0.95,
tuned_model_name: Optional[str] = None,
verbose: bool = False,
) -> Union[BaseLLM, Callable]:
) -> BaseLanguageModel:
return VertexAI(
credentials=credentials,
location=location,

View file

@ -1,10 +1,7 @@
from .AmazonBedrockSpecs import AmazonBedrockComponent
from .AnthropicLLMSpecs import AnthropicLLM
from .AnthropicSpecs import AnthropicComponent
from .AzureChatOpenAISpecs import AzureChatOpenAISpecsComponent
from .BaiduQianfanChatEndpointsSpecs import QianfanChatEndpointComponent
from .BaiduQianfanLLMEndpointsSpecs import QianfanLLMEndpointComponent
from .ChatAnthropicSpecs import ChatAnthropicComponent
from .ChatAnthropicSpecs import AnthropicLLM
from .ChatLiteLLMSpecs import ChatLiteLLMComponent
from .ChatOllamaEndpointSpecs import ChatOllamaComponent
from .ChatOpenAISpecs import ChatOpenAIComponent
@ -17,12 +14,11 @@ from .VertexAISpecs import VertexAIComponent
__all__ = [
"AmazonBedrockComponent",
"AnthropicLLM",
"AnthropicComponent",
"ChatAntropicSpecsComponent",
"AzureChatOpenAISpecsComponent",
"QianfanChatEndpointComponent",
"QianfanLLMEndpointComponent",
"ChatAnthropicComponent",
"AnthropicLLM",
"ChatLiteLLMComponent",
"ChatOllamaComponent",
"ChatOpenAIComponent",

View file

@ -2,6 +2,7 @@ from typing import Optional
from langchain.llms.base import BaseLanguageModel
from langchain_openai import AzureChatOpenAI
from pydantic.v1 import SecretStr
from langflow.base.constants import STREAM_INFO_TEXT
from langflow.base.models.model import LCModelComponent
@ -105,13 +106,17 @@ class AzureChatOpenAIComponent(LCModelComponent):
max_tokens: Optional[int] = 1000,
stream: bool = False,
) -> BaseLanguageModel:
if api_key:
secret_api_key = SecretStr(api_key)
else:
secret_api_key = None
try:
output = AzureChatOpenAI(
model=model,
azure_endpoint=azure_endpoint,
azure_deployment=azure_deployment,
api_version=api_version,
api_key=api_key,
api_key=secret_api_key,
temperature=temperature,
max_tokens=max_tokens,
)

View file

@ -1,6 +1,7 @@
from typing import Optional
from langchain_openai import ChatOpenAI
from pydantic.v1 import SecretStr
from langflow.base.constants import STREAM_INFO_TEXT
from langflow.base.models.model import LCModelComponent
@ -94,12 +95,17 @@ class OpenAIModelComponent(LCModelComponent):
) -> Text:
if not openai_api_base:
openai_api_base = "https://api.openai.com/v1"
if openai_api_key:
api_key = SecretStr(openai_api_key)
else:
api_key = None
output = ChatOpenAI(
max_tokens=max_tokens,
model_kwargs=model_kwargs,
model=model_name,
base_url=openai_api_base,
api_key=openai_api_key,
api_key=api_key,
temperature=temperature,
)

View file

@ -19,11 +19,11 @@ class ChatOutput(ChatComponent):
return_record: Optional[bool] = False,
record_template: Optional[str] = "{text}",
) -> Union[Text, Record]:
return super().build(
return super().build_with_record(
sender=sender,
sender_name=sender_name,
input_value=input_value,
session_id=session_id,
return_record=return_record,
record_template=record_template,
record_template=record_template or "",
)

View file

@ -24,5 +24,5 @@ class TextOutput(TextComponent):
},
}
def build(self, input_value: Optional[Text] = "", record_template: str = "") -> Text:
def build(self, input_value: Optional[Text] = "", record_template: Optional[str] = "") -> Text:
return super().build(input_value=input_value, record_template=record_template)

View file

@ -1,8 +1,8 @@
from typing import Callable, Optional, Union
from typing import Optional
from langchain.retrievers import MultiQueryRetriever
from langflow.field_typing import BaseLLM, BaseRetriever, PromptTemplate
from langflow.field_typing import BaseRetriever, PromptTemplate, BaseLanguageModel
from langflow.interface.custom.custom_component import CustomComponent
@ -39,11 +39,11 @@ class MultiQueryRetrieverComponent(CustomComponent):
def build(
self,
llm: BaseLLM,
llm: BaseLanguageModel,
retriever: BaseRetriever,
prompt: Optional[PromptTemplate] = None,
parser_key: str = "lines",
) -> Union[Callable, MultiQueryRetriever]:
) -> MultiQueryRetriever:
if not prompt:
return MultiQueryRetriever.from_llm(llm=llm, retriever=retriever, parser_key=parser_key)
else:

View file

@ -1,5 +1,3 @@
from typing import Callable, Union
from langchain.agents.agent_toolkits.vectorstore.toolkit import VectorStoreInfo
from langchain_community.vectorstores import VectorStore
@ -22,5 +20,5 @@ class VectorStoreInfoComponent(CustomComponent):
vectorstore: VectorStore,
description: str,
name: str,
) -> Union[VectorStoreInfo, Callable]:
) -> VectorStoreInfo:
return VectorStoreInfo(vectorstore=vectorstore, description=description, name=name)

View file

@ -0,0 +1,68 @@
import importlib
from langchain.agents import Tool
from langchain_experimental.utilities import PythonREPL
from langflow.base.tools.base import build_status_from_tool
from langflow.custom import CustomComponent
class PythonREPLToolComponent(CustomComponent):
display_name = "Python REPL Tool"
description = "A tool for running Python code in a REPL environment."
def build_config(self):
return {
"name": {"display_name": "Name", "info": "The name of the tool."},
"description": {"display_name": "Description", "info": "A description of the tool."},
"global_imports": {
"display_name": "Global Imports",
"info": "A list of modules to import globally, e.g. ['math', 'numpy'].",
},
}
def get_globals(self, globals: list[str]) -> dict:
"""
Retrieves the global variables from the specified modules.
Args:
globals (list[str]): A list of module names.
Returns:
dict: A dictionary containing the global variables from the specified modules.
"""
global_dict = {}
for module in globals:
try:
imported_module = importlib.import_module(module)
global_dict[imported_module.__name__] = imported_module
except ImportError:
print(f"Could not import module {module}")
return global_dict
def build(
self,
name: str = "python_repl",
description: str = "A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`.",
global_imports: list[str] = ["math"],
) -> Tool:
"""
Builds a Python REPL tool.
Args:
name (str, optional): The name of the tool. Defaults to "python_repl".
description (str, optional): The description of the tool. Defaults to "A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`. ".
global_imports (list[str], optional): A list of global imports to be available in the Python REPL. Defaults to ["math"].
Returns:
Tool: The built Python REPL tool.
"""
_globals = self.get_globals(global_imports)
python_repl = PythonREPL(_globals=_globals)
tool = Tool(
name=name,
description=description,
func=python_repl.run,
)
self.status = build_status_from_tool(tool)
return tool

View file

@ -1,5 +1,7 @@
from .PythonREPLTool import PythonREPLToolComponent
from .RetrieverTool import RetrieverToolComponent
from .SearchAPITool import SearchApiToolComponent
from .SearchApi import SearchApi
from .SearchAPITool import SearchApiToolComponent
__all__ = ["RetrieverToolComponent", "SearchApiToolComponent", "SearchApi"]
__all__ = ["RetrieverToolComponent", "SearchApiToolComponent", "SearchApi", "PythonREPLToolComponent"]

View file

@ -3,6 +3,7 @@ from typing import List, Optional
from langflow.components.vectorstores.base.model import LCVectorStoreComponent
from langflow.components.vectorstores.Pinecone import PineconeComponent
from langflow.field_typing import Embeddings, Text
from langflow.field_typing.constants import NestedDict
from langflow.schema import Record
@ -32,7 +33,6 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
"default": "",
"required": True,
},
"search_kwargs": {"display_name": "Search Kwargs", "default": "{}"},
"pool_threads": {
"display_name": "Pool Threads",
"default": 1,
@ -57,6 +57,7 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
pinecone_api_key: Optional[str] = None,
namespace: Optional[str] = "default",
search_type: str = "similarity",
search_kwargs: Optional[NestedDict] = None,
) -> List[Record]: # type: ignore[override]
vector_store = super().build(
embedding=embedding,
@ -70,7 +71,13 @@ class PineconeSearchComponent(PineconeComponent, LCVectorStoreComponent):
)
if not vector_store:
raise ValueError("Failed to load the Pinecone index.")
if search_kwargs is None:
search_kwargs = {}
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type, k=number_of_results
vector_store=vector_store,
input_value=input_value,
search_type=search_type,
k=number_of_results,
**search_kwargs,
)

View file

@ -86,13 +86,18 @@ class QdrantSearchComponent(QdrantComponent, LCVectorStoreComponent):
port=port,
prefer_grpc=prefer_grpc,
prefix=prefix,
search_kwargs=search_kwargs,
timeout=timeout,
url=url,
)
if not vector_store:
raise ValueError("Failed to load the Qdrant index.")
if search_kwargs is None:
search_kwargs = {}
return self.search_with_vector_store(
vector_store=vector_store, input_value=input_value, search_type=search_type, k=number_of_results
vector_store=vector_store,
input_value=input_value,
search_type=search_type,
k=number_of_results,
**search_kwargs,
)

View file

@ -105,7 +105,7 @@ class AstraDBVectorStoreComponent(CustomComponent):
bulk_insert_batch_concurrency: Optional[int] = None,
bulk_insert_overwrite_concurrency: Optional[int] = None,
bulk_delete_concurrency: Optional[int] = None,
setup_mode: str = "Async",
setup_mode: str = "Sync",
pre_delete_collection: bool = False,
metadata_indexing_include: Optional[List[str]] = None,
metadata_indexing_exclude: Optional[List[str]] = None,

View file

@ -1,7 +1,7 @@
from typing import List, Optional
from langchain_community.vectorstores.mongodb_atlas import MongoDBAtlasVectorSearch
from langflow.field_typing import Embeddings, NestedDict
from langflow.field_typing import Embeddings
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema.schema import Record
@ -19,7 +19,6 @@ class MongoDBAtlasComponent(CustomComponent):
"db_name": {"display_name": "Database Name"},
"index_name": {"display_name": "Index Name"},
"mongodb_atlas_cluster_uri": {"display_name": "MongoDB Atlas Cluster URI"},
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
}
def build(
@ -30,9 +29,7 @@ class MongoDBAtlasComponent(CustomComponent):
db_name: str = "",
index_name: str = "",
mongodb_atlas_cluster_uri: str = "",
search_kwargs: Optional[NestedDict] = None,
) -> MongoDBAtlasVectorSearch:
search_kwargs = search_kwargs or {}
try:
from pymongo import MongoClient
except ImportError:
@ -56,7 +53,6 @@ class MongoDBAtlasComponent(CustomComponent):
db_name=db_name,
index_name=index_name,
mongodb_atlas_cluster_uri=mongodb_atlas_cluster_uri,
search_kwargs=search_kwargs,
)
else:
vector_store = MongoDBAtlasVectorSearch(

View file

@ -33,7 +33,6 @@ class PineconeComponent(CustomComponent):
"default": "",
"required": True,
},
"search_kwargs": {"display_name": "Search Kwargs", "default": "{}"},
"pool_threads": {
"display_name": "Pool Threads",
"default": 1,

View file

@ -4,7 +4,7 @@ from langchain.schema import BaseRetriever
from langchain_community.vectorstores import VectorStore
from langchain_community.vectorstores.qdrant import Qdrant
from langflow.field_typing import Embeddings, NestedDict
from langflow.field_typing import Embeddings
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema.schema import Record
@ -37,7 +37,6 @@ class QdrantComponent(CustomComponent):
"port": {"display_name": "Port", "advanced": True},
"prefer_grpc": {"display_name": "Prefer gRPC", "advanced": True},
"prefix": {"display_name": "Prefix", "advanced": True},
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
"timeout": {"display_name": "Timeout", "advanced": True},
"url": {"display_name": "URL", "advanced": True},
}
@ -59,7 +58,6 @@ class QdrantComponent(CustomComponent):
port: Optional[int] = 6333,
prefer_grpc: bool = False,
prefix: Optional[str] = None,
search_kwargs: Optional[NestedDict] = None,
timeout: Optional[int] = None,
url: Optional[str] = None,
) -> Union[VectorStore, Qdrant, BaseRetriever]:
@ -111,7 +109,6 @@ class QdrantComponent(CustomComponent):
port=port,
prefer_grpc=prefer_grpc,
prefix=prefix,
search_kwargs=search_kwargs,
timeout=timeout,
url=url,
)

View file

@ -5,7 +5,7 @@ from langchain_community.vectorstores import VectorStore
from langchain_community.vectorstores.supabase import SupabaseVectorStore
from supabase.client import Client, create_client
from langflow.field_typing import Embeddings, NestedDict
from langflow.field_typing import Embeddings
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema.schema import Record
@ -19,7 +19,6 @@ class SupabaseComponent(CustomComponent):
"inputs": {"display_name": "Input", "input_types": ["Document", "Record"]},
"embedding": {"display_name": "Embedding"},
"query_name": {"display_name": "Query Name"},
"search_kwargs": {"display_name": "Search Kwargs", "advanced": True},
"supabase_service_key": {"display_name": "Supabase Service Key"},
"supabase_url": {"display_name": "Supabase URL"},
"table_name": {"display_name": "Table Name", "advanced": True},
@ -30,7 +29,6 @@ class SupabaseComponent(CustomComponent):
embedding: Embeddings,
inputs: Optional[List[Record]] = None,
query_name: str = "",
search_kwargs: NestedDict = {},
supabase_service_key: str = "",
supabase_url: str = "",
table_name: str = "",
@ -46,7 +44,6 @@ class SupabaseComponent(CustomComponent):
documents=documents,
embedding=embedding,
query_name=query_name,
search_kwargs=search_kwargs,
client=supabase,
table_name=table_name,
)

View file

@ -249,7 +249,10 @@ class Graph:
vertex.update_raw_params({"session_id": session_id})
# Process the graph
try:
await self.process()
start_component_id = next(
(vertex_id for vertex_id in self._is_input_vertices if "chat" in vertex_id.lower()), None
)
await self.process(start_component_id=start_component_id)
self.increment_run_count()
except Exception as exc:
logger.exception(exc)
@ -293,18 +296,27 @@ class Graph:
# run the async function in a sync way
# this could be used in a FastAPI endpoint
# so we should take care of the event loop
loop = asyncio.get_event_loop()
return loop.run_until_complete(
self.arun(
inputs=inputs,
inputs_components=input_components,
types=types,
outputs=outputs,
session_id=session_id,
stream=stream,
)
coro = self.arun(
inputs=inputs,
inputs_components=input_components,
types=types,
outputs=outputs,
session_id=session_id,
stream=stream,
)
try:
# Attempt to get the running event loop; if none, an exception is raised
loop = asyncio.get_running_loop()
if loop.is_closed():
raise RuntimeError("The running event loop is closed.")
except RuntimeError:
# If there's no running event loop or it's closed, use asyncio.run
return asyncio.run(coro)
# If there's an existing, open event loop, use it to run the async function
return loop.run_until_complete(coro)
async def arun(
self,
inputs: list[Dict[str, str]],
@ -345,7 +357,7 @@ class Graph:
if types is None:
types = []
for _ in range(len(inputs) - len(types)):
types.append("any")
types.append("chat") # default to chat
for run_inputs, components, input_type in zip(inputs, inputs_components, types):
run_outputs = await self._run(
inputs=run_inputs,
@ -733,8 +745,10 @@ class Graph:
vertices.append(vertex)
return vertices
async def process(self) -> "Graph":
async def process(self, start_component_id: Optional[str] = None) -> "Graph":
"""Processes the graph with vertices in each layer run in parallel."""
self.sort_vertices(start_component_id=start_component_id)
vertices_layers = self.sorted_vertices_layers
vertex_task_run_count: Dict[str, int] = {}
for layer_index, layer in enumerate(vertices_layers):
@ -904,7 +918,7 @@ class Graph:
return ChatVertex
elif node_name in ["ShouldRunNext"]:
return RoutingVertex
elif node_name in ["SharedState", "Notify", "GetNotified"]:
elif node_name in ["SharedState", "Notify", "Listen"]:
return StateVertex
elif node_base_type in lazy_load_vertex_dict.VERTEX_TYPE_MAP:
return lazy_load_vertex_dict.VERTEX_TYPE_MAP[node_base_type]
@ -1116,6 +1130,34 @@ class Graph:
return vertices_layers
def sort_layer_by_dependency(self, vertices_layers: List[List[str]]) -> List[List[str]]:
"""Sorts the vertices in each layer by dependency, ensuring no vertex depends on a subsequent vertex."""
sorted_layers = []
for layer in vertices_layers:
sorted_layer = self._sort_single_layer_by_dependency(layer)
sorted_layers.append(sorted_layer)
return sorted_layers
def _sort_single_layer_by_dependency(self, layer: List[str]) -> List[str]:
"""Sorts a single layer by dependency using a stable sorting method."""
# Build a map of each vertex to its index in the layer for quick lookup.
index_map = {vertex: index for index, vertex in enumerate(layer)}
# Create a sorted copy of the layer based on dependency order.
sorted_layer = sorted(layer, key=lambda vertex: self._max_dependency_index(vertex, index_map), reverse=True)
return sorted_layer
def _max_dependency_index(self, vertex_id: str, index_map: Dict[str, int]) -> int:
"""Finds the highest index a given vertex's dependencies occupy in the same layer."""
vertex = self.get_vertex(vertex_id)
max_index = -1
for successor in vertex.successors: # Assuming vertex.successors is a list of successor vertex identifiers.
if successor.id in index_map:
max_index = max(max_index, index_map[successor.id])
return max_index
def sort_vertices(
self,
stop_component_id: Optional[str] = None,
@ -1137,6 +1179,9 @@ class Graph:
vertices_layers = self.layered_topological_sort(vertices)
vertices_layers = self.sort_by_avg_build_time(vertices_layers)
# vertices_layers = self.sort_chat_inputs_first(vertices_layers)
# Now we should sort each layer in a way that we make sure
# vertex V does not depend on vertex V+1
vertices_layers = self.sort_layer_by_dependency(vertices_layers)
self.increment_run_count()
self._sorted_vertices_layers = vertices_layers
first_layer = vertices_layers[0]

View file

@ -1,15 +1,22 @@
from typing import TYPE_CHECKING, Callable
from langflow.services.deps import get_state_service
from loguru import logger
from langflow.services.deps import get_settings_service, get_state_service
if TYPE_CHECKING:
from langflow.services.state.service import StateService
class GraphStateManager:
def __init__(self):
self.state_service: "StateService" = get_state_service()
try:
self.state_service: "StateService" = get_state_service()
except Exception as e:
logger.debug(f"Error getting state service. Defaulting to InMemoryStateService: {e}")
from langflow.services.state.service import InMemoryStateService
self.state_service = InMemoryStateService(get_settings_service())
def append_state(self, key, new_state, run_id: str):
self.state_service.append_state(key, new_state, run_id)

View file

@ -1,10 +1,10 @@
import ast
import asyncio
import inspect
import os
import types
from enum import Enum
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Optional
import os
from loguru import logger
@ -315,7 +315,8 @@ class Vertex:
raise e
params[field_name] = full_path
elif field.get("required"):
raise ValueError(f"File path not found for {self.display_name}")
field_display_name = field.get("display_name")
raise ValueError(f"File path not found for {field_display_name} in component {self.display_name}")
elif field.get("type") in DIRECT_TYPES and params.get(field_name) is None:
val = field.get("value")
if field.get("type") == "code":

View file

@ -1,11 +1,10 @@
from typing import TYPE_CHECKING, Any, Callable, Coroutine, List, Optional, Tuple, Type, Union, cast
from pydantic.v1 import BaseModel, Field, create_model
from sqlmodel import select
from typing import TYPE_CHECKING, Any, Awaitable, Callable, List, Optional, Tuple, Type, Union, cast
from langflow.schema.schema import INPUT_FIELD_NAME, Record
from langflow.services.database.models.flow.model import Flow
from langflow.services.deps import session_scope
from pydantic.v1 import BaseModel, Field, create_model
from sqlmodel import select
if TYPE_CHECKING:
from langflow.graph.graph.base import Graph
@ -86,7 +85,7 @@ async def run_flow(
return await graph.arun(inputs_list, inputs_components=inputs_components, types=types)
def generate_function_for_flow(inputs: List["Vertex"], flow_id: str) -> Coroutine:
def generate_function_for_flow(inputs: List["Vertex"], flow_id: str) -> Callable[..., Awaitable[Any]]:
"""
Generate a dynamic flow function based on the given inputs and flow ID.
@ -145,7 +144,9 @@ async def flow_function({func_args}):
return local_scope["flow_function"]
def build_function_and_schema(flow_record: Record, graph: "Graph") -> Tuple[Callable, Type[BaseModel]]:
def build_function_and_schema(
flow_record: Record, graph: "Graph"
) -> Tuple[Callable[..., Awaitable[Any]], Type[BaseModel]]:
"""
Builds a dynamic function and schema for a given flow.

View file

@ -84,7 +84,7 @@ def log_node_changes(node_changes_log):
logger.debug("\n".join(formatted_messages))
def load_starter_projects():
def load_starter_projects() -> list[tuple[Path, dict]]:
starter_projects = []
folder = Path(__file__).parent / "starter_projects"
for file in folder.glob("*.json"):

Some files were not shown because too many files have changed in this diff Show more