Merge remote-tracking branch 'origin/dev' into node-shortcuts-refactor
This commit is contained in:
commit
23bcdcec28
216 changed files with 11018 additions and 10942 deletions
|
|
@ -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=
|
||||
|
|
|
|||
94
.github/actions/poetry_caching/action.yml
vendored
Normal file
94
.github/actions/poetry_caching/action.yml
vendored
Normal 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)) }}
|
||||
44
.github/workflows/ci.yml
vendored
44
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
22
.github/workflows/lint.yml
vendored
22
.github/workflows/lint.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
31
.github/workflows/pre-release-base.yml
vendored
31
.github/workflows/pre-release-base.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
43
.github/workflows/pre-release-langflow.yml
vendored
43
.github/workflows/pre-release-langflow.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
13
.github/workflows/python_test.yml
vendored
13
.github/workflows/python_test.yml
vendored
|
|
@ -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"
|
||||
|
|
|
|||
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
|
|
@ -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
3
.gitignore
vendored
|
|
@ -264,4 +264,5 @@ scratchpad*
|
|||
chroma*/*
|
||||
stuff/*
|
||||
src/frontend/playwright-report/index.html
|
||||
*.bak
|
||||
*.bak
|
||||
prof/*
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
[](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial.md)
|
||||
[](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
|
||||
|
||||
[](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial_spot.md)
|
||||
[](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 |
|
||||
|
|
|
|||
20
Makefile
20
Makefile
|
|
@ -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
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -1,9 +1,11 @@
|
|||
<!-- markdownlint-disable MD030 -->
|
||||
|
||||
# [](https://www.langflow.org)
|
||||
# [](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.
|
||||
|
||||
[](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)
|
||||
[](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
|
|||
|
||||
---
|
||||
|
||||
[](https://star-history.com/#logspace-ai/langflow&Date)
|
||||
[](https://star-history.com/#langflow-ai/langflow&Date)
|
||||
|
||||
# 🌟 Contributors
|
||||
|
||||
[](https://github.com/logspace-ai/langflow/graphs/contributors)
|
||||
[](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.
|
||||
|
|
|
|||
|
|
@ -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 -
|
||||
|
||||
|
|
|
|||
|
|
@ -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 -
|
||||
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
3
docker_example/pre.Dockerfile
Normal file
3
docker_example/pre.Dockerfile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
FROM logspace/langflow:1.0-alpha
|
||||
|
||||
CMD ["python", "-m", "langflow", "run", "--host", "0.0.0.0", "--port", "7860"]
|
||||
30
docker_example/pre.docker-compose.yml
Normal file
30
docker_example/pre.docker-compose.yml
Normal 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:
|
||||
|
|
@ -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`_ |
|
||||
|
|
|
|||
|
|
@ -123,7 +123,12 @@ Used to load [OpenAI’s](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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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! 🚀
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
[](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/logspace-ai/langflow&working_dir=scripts&shellonly=true&tutorial=walkthroughtutorial.md)
|
||||
[](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 |
|
||||
|
|
|
|||
0
docs/docs/getting-started/basic-prompting.mdx
Normal file
0
docs/docs/getting-started/basic-prompting.mdx
Normal file
0
docs/docs/getting-started/blog-writer.mdx
Normal file
0
docs/docs/getting-started/blog-writer.mdx
Normal file
0
docs/docs/getting-started/document-qa.mdx
Normal file
0
docs/docs/getting-started/document-qa.mdx
Normal file
0
docs/docs/getting-started/memory-chatbot.mdx
Normal file
0
docs/docs/getting-started/memory-chatbot.mdx
Normal file
195
docs/docs/getting-started/rag-with-astradb.mdx
Normal file
195
docs/docs/getting-started/rag-with-astradb.mdx
Normal 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.
|
||||
|
|
@ -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"; |
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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={{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
812
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|||

|
||||
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` を開きます。
|
||||

|
||||

|
||||
|
||||
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`で設定されます。
|
||||

|
||||

|
||||
|
||||
# 環境の削除
|
||||
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` を選択して削除します。
|
||||

|
||||

|
||||
|
|
|
|||
|
|
@ -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 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#/).
|
||||

|
||||
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.
|
||||

|
||||
s
|
||||
s
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 ###
|
||||
|
|
@ -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 ###
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -24,4 +24,5 @@ FIELD_FORMAT_ATTRIBUTES = [
|
|||
"real_time_refresh",
|
||||
"refresh_button",
|
||||
"refresh_button_text",
|
||||
"options",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
83
src/backend/base/langflow/base/prompts/api_utils.py
Normal file
83
src/backend/base/langflow/base/prompts/api_utils.py
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
0
src/backend/base/langflow/base/tools/__init__.py
Normal file
0
src/backend/base/langflow/base/tools/__init__.py
Normal file
23
src/backend/base/langflow/base/tools/base.py
Normal file
23
src/backend/base/langflow/base/tools/base.py
Normal 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 "")
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 "",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
68
src/backend/base/langflow/components/tools/PythonREPLTool.py
Normal file
68
src/backend/base/langflow/components/tools/PythonREPLTool.py
Normal 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
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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"):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue