Merge branch 'zustand/io/migration' of personal:logspace-ai/langflow into zustand/io/migration
This commit is contained in:
commit
0f22175d6a
49 changed files with 1606 additions and 1241 deletions
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Set update schedule for GitHub Actions
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
# Check for updates to GitHub Actions every week
|
||||||
|
interval: "monthly"
|
||||||
|
|
||||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -16,10 +16,10 @@ jobs:
|
||||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
|
|
||||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
|
|
@ -30,11 +30,11 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
|
@ -48,7 +48,7 @@ jobs:
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
@ -61,6 +61,6 @@ jobs:
|
||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|
|
||||||
4
.github/workflows/deploy_gh-pages.yml
vendored
4
.github/workflows/deploy_gh-pages.yml
vendored
|
|
@ -12,8 +12,8 @@ jobs:
|
||||||
name: Deploy to GitHub Pages
|
name: Deploy to GitHub Pages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
|
||||||
5
.github/workflows/lint.yml
vendored
5
.github/workflows/lint.yml
vendored
|
|
@ -16,13 +16,14 @@ jobs:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.9"
|
- "3.9"
|
||||||
- "3.10"
|
- "3.10"
|
||||||
|
- "3.11"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: |
|
run: |
|
||||||
pipx install poetry==$POETRY_VERSION
|
pipx install poetry==$POETRY_VERSION
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: poetry
|
cache: poetry
|
||||||
|
|
|
||||||
4
.github/workflows/pre-release.yml
vendored
4
.github/workflows/pre-release.yml
vendored
|
|
@ -18,11 +18,11 @@ jobs:
|
||||||
if: ${{ (github.event.pull_request.merged == true) && contains(github.event.pull_request.labels.*.name, 'pre-release') }}
|
if: ${{ (github.event.pull_request.merged == true) && contains(github.event.pull_request.labels.*.name, 'pre-release') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: pipx install poetry==$POETRY_VERSION
|
run: pipx install poetry==$POETRY_VERSION
|
||||||
- name: Set up Python 3.10
|
- name: Set up Python 3.10
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
|
|
|
||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -17,11 +17,11 @@ jobs:
|
||||||
if: ${{ (github.event.pull_request.merged == true) && contains(github.event.pull_request.labels.*.name, 'Release') }}
|
if: ${{ (github.event.pull_request.merged == true) && contains(github.event.pull_request.labels.*.name, 'Release') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: pipx install poetry==$POETRY_VERSION
|
run: pipx install poetry==$POETRY_VERSION
|
||||||
- name: Set up Python 3.10
|
- name: Set up Python 3.10
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
|
|
|
||||||
5
.github/workflows/test.yml
vendored
5
.github/workflows/test.yml
vendored
|
|
@ -16,14 +16,15 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.10"
|
- "3.10"
|
||||||
|
- "3.11"
|
||||||
env:
|
env:
|
||||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: pipx install poetry==$POETRY_VERSION
|
run: pipx install poetry==$POETRY_VERSION
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ services:
|
||||||
dockerfile: ./cdk.Dockerfile
|
dockerfile: ./cdk.Dockerfile
|
||||||
args:
|
args:
|
||||||
- BACKEND_URL=http://backend:7860
|
- BACKEND_URL=http://backend:7860
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
environment:
|
environment:
|
||||||
- VITE_PROXY_TARGET=http://backend:7860
|
- VITE_PROXY_TARGET=http://backend:7860
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
2076
poetry.lock
generated
2076
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "langflow"
|
name = "langflow"
|
||||||
version = "0.6.7a1"
|
version = "0.6.7a2"
|
||||||
description = "A Python package with a built-in web application"
|
description = "A Python package with a built-in web application"
|
||||||
authors = ["Logspace <contact@logspace.ai>"]
|
authors = ["Logspace <contact@logspace.ai>"]
|
||||||
maintainers = [
|
maintainers = [
|
||||||
|
|
@ -25,18 +25,20 @@ documentation = "https://docs.langflow.org"
|
||||||
langflow = "langflow.__main__:main"
|
langflow = "langflow.__main__:main"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = ">=3.9,<3.11"
|
|
||||||
|
|
||||||
|
python = ">=3.9,<3.12"
|
||||||
|
duckdb = "^0.9.2"
|
||||||
fastapi = "^0.109.0"
|
fastapi = "^0.109.0"
|
||||||
uvicorn = "^0.27.0"
|
uvicorn = "^0.27.0"
|
||||||
beautifulsoup4 = "^4.12.2"
|
beautifulsoup4 = "^4.12.2"
|
||||||
google-search-results = "^2.4.1"
|
google-search-results = "^2.4.1"
|
||||||
google-api-python-client = "^2.79.0"
|
google-api-python-client = "^2.118.0"
|
||||||
typer = "^0.9.0"
|
typer = "^0.9.0"
|
||||||
gunicorn = "^21.2.0"
|
gunicorn = "^21.2.0"
|
||||||
langchain = "~0.1.0"
|
langchain = "~0.1.0"
|
||||||
duckdb = "^0.9.2"
|
openai = "^1.12.0"
|
||||||
openai = "^1.11.0"
|
pandas = "2.2.0"
|
||||||
pandas = "2.0.3"
|
|
||||||
chromadb = "^0.4.0"
|
chromadb = "^0.4.0"
|
||||||
huggingface-hub = { version = "^0.20.0", extras = ["inference"] }
|
huggingface-hub = { version = "^0.20.0", extras = ["inference"] }
|
||||||
rich = "^13.7.0"
|
rich = "^13.7.0"
|
||||||
|
|
@ -50,15 +52,14 @@ fake-useragent = "^1.4.0"
|
||||||
docstring-parser = "^0.15"
|
docstring-parser = "^0.15"
|
||||||
psycopg2-binary = "^2.9.6"
|
psycopg2-binary = "^2.9.6"
|
||||||
pyarrow = "^14.0.0"
|
pyarrow = "^14.0.0"
|
||||||
tiktoken = "~0.5.0"
|
tiktoken = "~0.6.0"
|
||||||
wikipedia = "^1.4.0"
|
wikipedia = "^1.4.0"
|
||||||
qdrant-client = "^1.7.0"
|
qdrant-client = "^1.7.0"
|
||||||
weaviate-client = "*"
|
weaviate-client = "*"
|
||||||
jina = "*"
|
|
||||||
sentence-transformers = { version = "^2.3.1", optional = true }
|
sentence-transformers = { version = "^2.3.1", optional = true }
|
||||||
ctransformers = { version = "^0.2.10", optional = true }
|
ctransformers = { version = "^0.2.10", optional = true }
|
||||||
cohere = "^4.45.0"
|
cohere = "^4.47.0"
|
||||||
python-multipart = "^0.0.6"
|
python-multipart = "^0.0.7"
|
||||||
sqlmodel = "^0.0.14"
|
sqlmodel = "^0.0.14"
|
||||||
faiss-cpu = "^1.7.4"
|
faiss-cpu = "^1.7.4"
|
||||||
anthropic = "^0.15.0"
|
anthropic = "^0.15.0"
|
||||||
|
|
@ -67,17 +68,17 @@ multiprocess = "^0.70.14"
|
||||||
cachetools = "^5.3.1"
|
cachetools = "^5.3.1"
|
||||||
types-cachetools = "^5.3.0.5"
|
types-cachetools = "^5.3.0.5"
|
||||||
platformdirs = "^4.2.0"
|
platformdirs = "^4.2.0"
|
||||||
pinecone-client = "^2.2.2"
|
pinecone-client = "^3.0.3"
|
||||||
pymongo = "^4.6.0"
|
pymongo = "^4.6.0"
|
||||||
supabase = "^2.3.0"
|
supabase = "^2.3.0"
|
||||||
certifi = "^2023.11.17"
|
certifi = "^2023.11.17"
|
||||||
google-cloud-aiplatform = "^1.36.0"
|
google-cloud-aiplatform = "^1.42.0"
|
||||||
psycopg = "^3.1.9"
|
psycopg = "^3.1.9"
|
||||||
psycopg-binary = "^3.1.9"
|
psycopg-binary = "^3.1.9"
|
||||||
fastavro = "^1.8.0"
|
fastavro = "^1.8.0"
|
||||||
langchain-experimental = "*"
|
langchain-experimental = "*"
|
||||||
celery = { extras = ["redis"], version = "^5.3.6", optional = true }
|
celery = { extras = ["redis"], version = "^5.3.6", optional = true }
|
||||||
redis = { version = "^4.6.0", optional = true }
|
redis = { version = "^5.0.1", optional = true }
|
||||||
flower = { version = "^2.0.0", optional = true }
|
flower = { version = "^2.0.0", optional = true }
|
||||||
alembic = "^1.13.0"
|
alembic = "^1.13.0"
|
||||||
passlib = "^1.7.4"
|
passlib = "^1.7.4"
|
||||||
|
|
@ -90,46 +91,46 @@ zep-python = "*"
|
||||||
pywin32 = { version = "^306", markers = "sys_platform == 'win32'" }
|
pywin32 = { version = "^306", markers = "sys_platform == 'win32'" }
|
||||||
loguru = "^0.7.1"
|
loguru = "^0.7.1"
|
||||||
langfuse = "^2.9.0"
|
langfuse = "^2.9.0"
|
||||||
pillow = "^10.0.0"
|
pillow = "^10.2.0"
|
||||||
metal-sdk = "^2.4.0"
|
metal-sdk = "^2.5.0"
|
||||||
markupsafe = "^2.1.3"
|
markupsafe = "^2.1.3"
|
||||||
extract-msg = "^0.45.0"
|
extract-msg = "^0.47.0"
|
||||||
# jq is not available for windows
|
# jq is not available for windows
|
||||||
jq = { version = "^1.6.0", markers = "sys_platform != 'win32'" }
|
jq = { version = "^1.6.0", markers = "sys_platform != 'win32'" }
|
||||||
boto3 = "^1.34.0"
|
boto3 = "^1.34.0"
|
||||||
numexpr = "^2.8.6"
|
numexpr = "^2.8.6"
|
||||||
qianfan = "0.2.0"
|
qianfan = "0.3.0"
|
||||||
pgvector = "^0.2.3"
|
pgvector = "^0.2.3"
|
||||||
pyautogen = "^0.2.0"
|
pyautogen = "^0.2.0"
|
||||||
langchain-google-genai = "^0.0.6"
|
langchain-google-genai = "^0.0.6"
|
||||||
elasticsearch = "^8.11.1"
|
elasticsearch = "^8.12.0"
|
||||||
pytube = "^15.0.0"
|
pytube = "^15.0.0"
|
||||||
python-socketio = "^5.11.0"
|
python-socketio = "^5.11.0"
|
||||||
llama-index = "^0.9.44"
|
llama-index = "0.9.48"
|
||||||
langchain-openai = "^0.0.5"
|
langchain-openai = "^0.0.6"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest-asyncio = "^0.23.1"
|
pytest-asyncio = "^0.23.1"
|
||||||
types-redis = "^4.6.0.5"
|
types-redis = "^4.6.0.5"
|
||||||
ipykernel = "^6.27.0"
|
ipykernel = "^6.29.0"
|
||||||
mypy = "^1.8.0"
|
mypy = "^1.8.0"
|
||||||
ruff = "^0.1.5"
|
ruff = "^0.2.1"
|
||||||
httpx = "*"
|
httpx = "*"
|
||||||
pytest = "^7.4.2"
|
pytest = "^8.0.0"
|
||||||
types-requests = "^2.31.0"
|
types-requests = "^2.31.0"
|
||||||
requests = "^2.31.0"
|
requests = "^2.31.0"
|
||||||
pytest-cov = "^4.1.0"
|
pytest-cov = "^4.1.0"
|
||||||
pandas-stubs = "^2.0.0.230412"
|
pandas-stubs = "^2.1.4.231227"
|
||||||
types-pillow = "^9.5.0.2"
|
types-pillow = "^10.2.0.20240213"
|
||||||
types-pyyaml = "^6.0.12.8"
|
types-pyyaml = "^6.0.12.8"
|
||||||
types-python-jose = "^3.3.4.8"
|
types-python-jose = "^3.3.4.8"
|
||||||
types-passlib = "^1.7.7.13"
|
types-passlib = "^1.7.7.13"
|
||||||
locust = "^2.19.1"
|
locust = "^2.23.1"
|
||||||
pytest-mock = "^3.12.0"
|
pytest-mock = "^3.12.0"
|
||||||
pytest-xdist = "^3.5.0"
|
pytest-xdist = "^3.5.0"
|
||||||
types-pywin32 = "^306.0.0.4"
|
types-pywin32 = "^306.0.0.4"
|
||||||
types-google-cloud-ndb = "^2.2.0.0"
|
types-google-cloud-ndb = "^2.2.0.0"
|
||||||
pytest-sugar = "^0.9.7"
|
pytest-sugar = "^1.0.0"
|
||||||
pytest-instafail = "^0.5.0"
|
pytest-instafail = "^0.5.0"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@ import asyncio
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
|
|
||||||
from langchain.schema import AgentAction, AgentFinish
|
from langchain.schema import AgentAction, AgentFinish
|
||||||
|
from langchain_core.callbacks.base import (AsyncCallbackHandler,
|
||||||
|
BaseCallbackHandler)
|
||||||
from langflow.api.v1.schemas import ChatResponse, PromptResponse
|
from langflow.api.v1.schemas import ChatResponse, PromptResponse
|
||||||
from langflow.services.deps import get_chat_service
|
from langflow.services.deps import get_chat_service
|
||||||
from langflow.utils.util import remove_ansi_escape_codes
|
from langflow.utils.util import remove_ansi_escape_codes
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
from langflow import CustomComponent
|
from typing import Callable, Union
|
||||||
|
|
||||||
from langchain.chains import LLMCheckerChain
|
from langchain.chains import LLMCheckerChain
|
||||||
from typing import Union, Callable
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import (
|
from langflow.field_typing import BaseLanguageModel, Chain
|
||||||
BaseLanguageModel,
|
|
||||||
Chain,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LLMCheckerChainComponent(CustomComponent):
|
class LLMCheckerChainComponent(CustomComponent):
|
||||||
|
|
@ -21,4 +19,4 @@ class LLMCheckerChainComponent(CustomComponent):
|
||||||
self,
|
self,
|
||||||
llm: BaseLanguageModel,
|
llm: BaseLanguageModel,
|
||||||
) -> Union[Chain, Callable]:
|
) -> Union[Chain, Callable]:
|
||||||
return LLMCheckerChain(llm=llm)
|
return LLMCheckerChain.from_llm(llm=llm)
|
||||||
|
|
|
||||||
|
|
@ -45,4 +45,6 @@ class RetrievalQAComponent(CustomComponent):
|
||||||
self.status = runnable
|
self.status = runnable
|
||||||
result = runnable.invoke({input_key: inputs})
|
result = runnable.invoke({input_key: inputs})
|
||||||
result = result.content if hasattr(result, "content") else result
|
result = result.content if hasattr(result, "content") else result
|
||||||
|
# Result is a dict with keys "query", "result" and "source_documents"
|
||||||
|
# for now we just return the result
|
||||||
return result.get("result")
|
return result.get("result")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
from langflow import CustomComponent
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from langchain.docstore.document import Document
|
from langchain.docstore.document import Document
|
||||||
from typing import Optional, Dict, Any
|
from langchain.document_loaders.directory import DirectoryLoader
|
||||||
|
from langflow import CustomComponent
|
||||||
|
|
||||||
|
|
||||||
class DirectoryLoaderComponent(CustomComponent):
|
class DirectoryLoaderComponent(CustomComponent):
|
||||||
|
|
@ -23,20 +25,18 @@ class DirectoryLoaderComponent(CustomComponent):
|
||||||
self,
|
self,
|
||||||
glob: str,
|
glob: str,
|
||||||
path: str,
|
path: str,
|
||||||
load_hidden: Optional[bool] = False,
|
max_concurrency: int = 2,
|
||||||
max_concurrency: Optional[int] = 10,
|
load_hidden: bool = False,
|
||||||
metadata: Optional[dict] = {},
|
recursive: bool = True,
|
||||||
recursive: Optional[bool] = True,
|
silent_errors: bool = False,
|
||||||
silent_errors: Optional[bool] = False,
|
use_multithreading: bool = True,
|
||||||
use_multithreading: Optional[bool] = True,
|
) -> List[Document]:
|
||||||
) -> Document:
|
return DirectoryLoader(
|
||||||
return Document(
|
|
||||||
glob=glob,
|
glob=glob,
|
||||||
path=path,
|
path=path,
|
||||||
load_hidden=load_hidden,
|
load_hidden=load_hidden,
|
||||||
max_concurrency=max_concurrency,
|
max_concurrency=max_concurrency,
|
||||||
metadata=metadata,
|
|
||||||
recursive=recursive,
|
recursive=recursive,
|
||||||
silent_errors=silent_errors,
|
silent_errors=silent_errors,
|
||||||
use_multithreading=use_multithreading,
|
use_multithreading=use_multithreading,
|
||||||
)
|
).load()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from langchain_community.embeddings.huggingface import HuggingFaceInferenceAPIEmbeddings
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from pydantic.v1.types import SecretStr
|
||||||
|
|
||||||
|
|
||||||
|
class HuggingFaceInferenceAPIEmbeddingsComponent(CustomComponent):
|
||||||
|
display_name = "HuggingFaceInferenceAPIEmbeddings"
|
||||||
|
description = "HuggingFace sentence_transformers embedding models, API version."
|
||||||
|
documentation = "https://github.com/huggingface/text-embeddings-inference"
|
||||||
|
|
||||||
|
def build_config(self):
|
||||||
|
return {
|
||||||
|
"api_key": {"display_name": "API Key", "password": True, "advanced": True},
|
||||||
|
"api_url": {"display_name": "API URL", "advanced": True},
|
||||||
|
"model_name": {"display_name": "Model Name"},
|
||||||
|
"cache_folder": {"display_name": "Cache Folder", "advanced": True},
|
||||||
|
"encode_kwargs": {"display_name": "Encode Kwargs", "advanced": True, "field_type": "dict"},
|
||||||
|
"model_kwargs": {"display_name": "Model Kwargs", "field_type": "dict", "advanced": True},
|
||||||
|
"multi_process": {"display_name": "Multi Process", "advanced": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(
|
||||||
|
self,
|
||||||
|
api_key: Optional[str] = "",
|
||||||
|
api_url: str = "http://localhost:8080",
|
||||||
|
model_name: str = "BAAI/bge-large-en-v1.5",
|
||||||
|
cache_folder: Optional[str] = None,
|
||||||
|
encode_kwargs: Optional[Dict] = {},
|
||||||
|
model_kwargs: Optional[Dict] = {},
|
||||||
|
multi_process: bool = False,
|
||||||
|
) -> HuggingFaceInferenceAPIEmbeddings:
|
||||||
|
if api_key:
|
||||||
|
secret_api_key = SecretStr(api_key)
|
||||||
|
else:
|
||||||
|
raise ValueError("API Key is required")
|
||||||
|
return HuggingFaceInferenceAPIEmbeddings(
|
||||||
|
api_key=secret_api_key,
|
||||||
|
api_url=api_url,
|
||||||
|
model_name=model_name,
|
||||||
|
)
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from typing import Any, Callable, Dict, List, Optional, Union
|
from typing import Any, Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
from langchain_openai.embeddings.base import OpenAIEmbeddings
|
from langchain_openai.embeddings.base import OpenAIEmbeddings
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import NestedDict
|
from langflow.field_typing import NestedDict
|
||||||
|
from pydantic.v1.types import SecretStr
|
||||||
|
|
||||||
|
|
||||||
class OpenAIEmbeddingsComponent(CustomComponent):
|
class OpenAIEmbeddingsComponent(CustomComponent):
|
||||||
|
|
@ -67,7 +67,7 @@ class OpenAIEmbeddingsComponent(CustomComponent):
|
||||||
},
|
},
|
||||||
"skip_empty": {"display_name": "Skip Empty", "advanced": True},
|
"skip_empty": {"display_name": "Skip Empty", "advanced": True},
|
||||||
"tiktoken_model_name": {"display_name": "TikToken Model Name"},
|
"tiktoken_model_name": {"display_name": "TikToken Model Name"},
|
||||||
"tikToken_enable": {"display_name": "TikToken Enable"},
|
"tikToken_enable": {"display_name": "TikToken Enable", "advanced": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
|
|
@ -92,14 +92,17 @@ class OpenAIEmbeddingsComponent(CustomComponent):
|
||||||
request_timeout: Optional[float] = None,
|
request_timeout: Optional[float] = None,
|
||||||
show_progress_bar: bool = False,
|
show_progress_bar: bool = False,
|
||||||
skip_empty: bool = False,
|
skip_empty: bool = False,
|
||||||
tikToken_enable: bool = True,
|
tiktoken_enable: bool = True,
|
||||||
tiktoken_model_name: Optional[str] = None,
|
tiktoken_model_name: Optional[str] = None,
|
||||||
) -> Union[OpenAIEmbeddings, Callable]:
|
) -> Union[OpenAIEmbeddings, Callable]:
|
||||||
# This is to avoid errors with Vector Stores (e.g Chroma)
|
# This is to avoid errors with Vector Stores (e.g Chroma)
|
||||||
if disallowed_special == ["all"]:
|
if disallowed_special == ["all"]:
|
||||||
disallowed_special = "all"
|
disallowed_special = "all" # type: ignore
|
||||||
|
|
||||||
|
api_key = SecretStr(openai_api_key) if openai_api_key else None
|
||||||
|
|
||||||
return OpenAIEmbeddings(
|
return OpenAIEmbeddings(
|
||||||
tiktoken_enabled=tikToken_enable,
|
tiktoken_enabled=tiktoken_enable,
|
||||||
default_headers=default_headers,
|
default_headers=default_headers,
|
||||||
default_query=default_query,
|
default_query=default_query,
|
||||||
allowed_special=set(allowed_special),
|
allowed_special=set(allowed_special),
|
||||||
|
|
@ -112,7 +115,7 @@ class OpenAIEmbeddingsComponent(CustomComponent):
|
||||||
model=model,
|
model=model,
|
||||||
model_kwargs=model_kwargs,
|
model_kwargs=model_kwargs,
|
||||||
base_url=openai_api_base,
|
base_url=openai_api_base,
|
||||||
api_key=openai_api_key,
|
api_key=api_key,
|
||||||
openai_api_type=openai_api_type,
|
openai_api_type=openai_api_type,
|
||||||
api_version=openai_api_version,
|
api_version=openai_api_version,
|
||||||
organization=openai_organization,
|
organization=openai_organization,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from pydantic import SecretStr
|
from pydantic.v1.types import SecretStr
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from typing import Optional, Union, Callable
|
from typing import Optional, Union, Callable
|
||||||
from langflow.field_typing import BaseLanguageModel
|
from langflow.field_typing import BaseLanguageModel
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from langchain_google_genai import ChatGoogleGenerativeAI # type: ignore
|
from langchain_google_genai import ChatGoogleGenerativeAI # type: ignore
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import BaseLanguageModel, RangeSpec, TemplateField
|
from langflow.field_typing import BaseLanguageModel, RangeSpec, TemplateField
|
||||||
|
from pydantic.v1.types import SecretStr
|
||||||
|
|
||||||
|
|
||||||
class GoogleGenerativeAIComponent(CustomComponent):
|
class GoogleGenerativeAIComponent(CustomComponent):
|
||||||
|
|
@ -63,10 +63,10 @@ class GoogleGenerativeAIComponent(CustomComponent):
|
||||||
) -> BaseLanguageModel:
|
) -> BaseLanguageModel:
|
||||||
return ChatGoogleGenerativeAI(
|
return ChatGoogleGenerativeAI(
|
||||||
model=model,
|
model=model,
|
||||||
max_output_tokens=max_output_tokens or None,
|
max_output_tokens=max_output_tokens or None, # type: ignore
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
top_k=top_k or None,
|
top_k=top_k or None,
|
||||||
top_p=top_p or None,
|
top_p=top_p or None, # type: ignore
|
||||||
n=n or 1,
|
n=n or 1,
|
||||||
google_api_key=google_api_key,
|
google_api_key=SecretStr(google_api_key),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ class CharacterTextSplitterComponent(CustomComponent):
|
||||||
chunk_size: int = 1000,
|
chunk_size: int = 1000,
|
||||||
separator: str = "\n",
|
separator: str = "\n",
|
||||||
) -> List[Document]:
|
) -> List[Document]:
|
||||||
|
# separator may come escaped from the frontend
|
||||||
|
separator = separator.encode().decode("unicode_escape")
|
||||||
docs = CharacterTextSplitter(
|
docs = CharacterTextSplitter(
|
||||||
chunk_overlap=chunk_overlap,
|
chunk_overlap=chunk_overlap,
|
||||||
chunk_size=chunk_size,
|
chunk_size=chunk_size,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
|
from langchain_community.agent_toolkits.openapi.toolkit import BaseToolkit, OpenAPIToolkit
|
||||||
|
from langchain_community.utilities.requests import TextRequestsWrapper
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import AgentExecutor
|
from langflow.field_typing import AgentExecutor
|
||||||
from typing import Callable
|
|
||||||
from langchain_community.utilities.requests import TextRequestsWrapper
|
|
||||||
from langchain_community.agent_toolkits.openapi.toolkit import OpenAPIToolkit
|
|
||||||
|
|
||||||
|
|
||||||
class OpenAPIToolkitComponent(CustomComponent):
|
class OpenAPIToolkitComponent(CustomComponent):
|
||||||
|
|
@ -19,5 +18,5 @@ class OpenAPIToolkitComponent(CustomComponent):
|
||||||
self,
|
self,
|
||||||
json_agent: AgentExecutor,
|
json_agent: AgentExecutor,
|
||||||
requests_wrapper: TextRequestsWrapper,
|
requests_wrapper: TextRequestsWrapper,
|
||||||
) -> Callable:
|
) -> BaseToolkit:
|
||||||
return OpenAPIToolkit(json_agent=json_agent, requests_wrapper=requests_wrapper)
|
return OpenAPIToolkit(json_agent=json_agent, requests_wrapper=requests_wrapper)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from langflow import CustomComponent
|
from typing import Callable, Union
|
||||||
from typing import Union, Callable
|
|
||||||
from langchain_community.utilities.google_search import GoogleSearchAPIWrapper
|
from langchain_community.utilities.google_search import GoogleSearchAPIWrapper
|
||||||
|
from langflow import CustomComponent
|
||||||
|
|
||||||
|
|
||||||
class GoogleSearchAPIWrapperComponent(CustomComponent):
|
class GoogleSearchAPIWrapperComponent(CustomComponent):
|
||||||
|
|
@ -18,4 +19,4 @@ class GoogleSearchAPIWrapperComponent(CustomComponent):
|
||||||
google_api_key: str,
|
google_api_key: str,
|
||||||
google_cse_id: str,
|
google_cse_id: str,
|
||||||
) -> Union[GoogleSearchAPIWrapper, Callable]:
|
) -> Union[GoogleSearchAPIWrapper, Callable]:
|
||||||
return GoogleSearchAPIWrapper(google_api_key=google_api_key, google_cse_id=google_cse_id)
|
return GoogleSearchAPIWrapper(google_api_key=google_api_key, google_cse_id=google_cse_id) # type: ignore
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from langflow import CustomComponent
|
from typing import Dict
|
||||||
from typing import Dict, Optional
|
|
||||||
|
|
||||||
# Assuming the existence of GoogleSerperAPIWrapper class in the serper module
|
# Assuming the existence of GoogleSerperAPIWrapper class in the serper module
|
||||||
# If this class does not exist, you would need to create it or import the appropriate class from another module
|
# If this class does not exist, you would need to create it or import the appropriate class from another module
|
||||||
from langchain_community.utilities.google_serper import GoogleSerperAPIWrapper
|
from langchain_community.utilities.google_serper import GoogleSerperAPIWrapper
|
||||||
|
from langflow import CustomComponent
|
||||||
|
|
||||||
|
|
||||||
class GoogleSerperAPIWrapperComponent(CustomComponent):
|
class GoogleSerperAPIWrapperComponent(CustomComponent):
|
||||||
|
|
@ -42,6 +42,5 @@ class GoogleSerperAPIWrapperComponent(CustomComponent):
|
||||||
def build(
|
def build(
|
||||||
self,
|
self,
|
||||||
serper_api_key: str,
|
serper_api_key: str,
|
||||||
result_key_for_type: Optional[Dict[str, str]] = None,
|
|
||||||
) -> GoogleSerperAPIWrapper:
|
) -> GoogleSerperAPIWrapper:
|
||||||
return GoogleSerperAPIWrapper(result_key_for_type=result_key_for_type, serper_api_key=serper_api_key)
|
return GoogleSerperAPIWrapper(serper_api_key=serper_api_key)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ class ChromaComponent(CustomComponent):
|
||||||
description: str = "Implementation of Vector Store using Chroma"
|
description: str = "Implementation of Vector Store using Chroma"
|
||||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/chroma"
|
documentation = "https://python.langchain.com/docs/integrations/vectorstores/chroma"
|
||||||
beta: bool = True
|
beta: bool = True
|
||||||
|
icon = "Chroma"
|
||||||
|
|
||||||
def build_config(self):
|
def build_config(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -28,7 +29,7 @@ class ChromaComponent(CustomComponent):
|
||||||
return {
|
return {
|
||||||
"collection_name": {"display_name": "Collection Name", "value": "langflow"},
|
"collection_name": {"display_name": "Collection Name", "value": "langflow"},
|
||||||
"persist": {"display_name": "Persist"},
|
"persist": {"display_name": "Persist"},
|
||||||
"persist_directory": {"display_name": "Persist Directory"},
|
"index_directory": {"display_name": "Persist Directory"},
|
||||||
"code": {"advanced": True, "display_name": "Code"},
|
"code": {"advanced": True, "display_name": "Code"},
|
||||||
"documents": {"display_name": "Documents", "is_list": True},
|
"documents": {"display_name": "Documents", "is_list": True},
|
||||||
"embedding": {"display_name": "Embedding"},
|
"embedding": {"display_name": "Embedding"},
|
||||||
|
|
@ -54,7 +55,7 @@ class ChromaComponent(CustomComponent):
|
||||||
persist: bool,
|
persist: bool,
|
||||||
embedding: Embeddings,
|
embedding: Embeddings,
|
||||||
chroma_server_ssl_enabled: bool,
|
chroma_server_ssl_enabled: bool,
|
||||||
persist_directory: Optional[str] = None,
|
index_directory: Optional[str] = None,
|
||||||
documents: Optional[List[Document]] = None,
|
documents: Optional[List[Document]] = None,
|
||||||
chroma_server_cors_allow_origins: Optional[str] = None,
|
chroma_server_cors_allow_origins: Optional[str] = None,
|
||||||
chroma_server_host: Optional[str] = None,
|
chroma_server_host: Optional[str] = None,
|
||||||
|
|
@ -66,7 +67,7 @@ class ChromaComponent(CustomComponent):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
- collection_name (str): The name of the collection.
|
- collection_name (str): The name of the collection.
|
||||||
- persist_directory (Optional[str]): The directory to persist the Vector Store to.
|
- index_directory (Optional[str]): The directory to persist the Vector Store to.
|
||||||
- chroma_server_ssl_enabled (bool): Whether to enable SSL for the Chroma server.
|
- chroma_server_ssl_enabled (bool): Whether to enable SSL for the Chroma server.
|
||||||
- persist (bool): Whether to persist the Vector Store or not.
|
- persist (bool): Whether to persist the Vector Store or not.
|
||||||
- embedding (Optional[Embeddings]): The embeddings to use for the Vector Store.
|
- embedding (Optional[Embeddings]): The embeddings to use for the Vector Store.
|
||||||
|
|
@ -85,7 +86,8 @@ class ChromaComponent(CustomComponent):
|
||||||
|
|
||||||
if chroma_server_host is not None:
|
if chroma_server_host is not None:
|
||||||
chroma_settings = chromadb.config.Settings(
|
chroma_settings = chromadb.config.Settings(
|
||||||
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins or None,
|
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins
|
||||||
|
or None,
|
||||||
chroma_server_host=chroma_server_host,
|
chroma_server_host=chroma_server_host,
|
||||||
chroma_server_port=chroma_server_port or None,
|
chroma_server_port=chroma_server_port or None,
|
||||||
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
||||||
|
|
@ -93,15 +95,25 @@ class ChromaComponent(CustomComponent):
|
||||||
)
|
)
|
||||||
|
|
||||||
# If documents, then we need to create a Chroma instance using .from_documents
|
# If documents, then we need to create a Chroma instance using .from_documents
|
||||||
|
|
||||||
|
# Check index_directory and expand it if it is a relative path
|
||||||
|
|
||||||
|
index_directory = self.resolve_path(index_directory)
|
||||||
|
|
||||||
if documents is not None and embedding is not None:
|
if documents is not None and embedding is not None:
|
||||||
if len(documents) == 0:
|
if len(documents) == 0:
|
||||||
raise ValueError("If documents are provided, there must be at least one document.")
|
raise ValueError(
|
||||||
return Chroma.from_documents(
|
"If documents are provided, there must be at least one document."
|
||||||
|
)
|
||||||
|
chroma = Chroma.from_documents(
|
||||||
documents=documents, # type: ignore
|
documents=documents, # type: ignore
|
||||||
persist_directory=persist_directory if persist else None,
|
persist_directory=index_directory if persist else None,
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
embedding=embedding,
|
embedding=embedding,
|
||||||
client_settings=chroma_settings,
|
client_settings=chroma_settings,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
return Chroma(persist_directory=persist_directory, client_settings=chroma_settings)
|
chroma = Chroma(
|
||||||
|
persist_directory=index_directory, client_settings=chroma_settings
|
||||||
|
)
|
||||||
|
return chroma
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import List, Optional
|
||||||
import chromadb # type: ignore
|
import chromadb # type: ignore
|
||||||
from langchain_community.vectorstores.chroma import Chroma
|
from langchain_community.vectorstores.chroma import Chroma
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import Document, Embeddings, Text
|
from langflow.field_typing import Embeddings, Text
|
||||||
from langflow.schema import Record, docs_to_records
|
from langflow.schema import Record, docs_to_records
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -15,6 +15,7 @@ class ChromaSearchComponent(CustomComponent):
|
||||||
display_name: str = "Chroma Search"
|
display_name: str = "Chroma Search"
|
||||||
description: str = "Search a Chroma collection for similar documents."
|
description: str = "Search a Chroma collection for similar documents."
|
||||||
beta: bool = True
|
beta: bool = True
|
||||||
|
icon = "Chroma"
|
||||||
|
|
||||||
def build_config(self):
|
def build_config(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -25,13 +26,19 @@ class ChromaSearchComponent(CustomComponent):
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
"inputs": {"display_name": "Input"},
|
"inputs": {"display_name": "Input"},
|
||||||
"search_type": {"display_name": "Search Type", "options": ["Similarity", "MMR"]},
|
"search_type": {
|
||||||
|
"display_name": "Search Type",
|
||||||
|
"options": ["Similarity", "MMR"],
|
||||||
|
},
|
||||||
"collection_name": {"display_name": "Collection Name", "value": "langflow"},
|
"collection_name": {"display_name": "Collection Name", "value": "langflow"},
|
||||||
"persist": {"display_name": "Persist"},
|
# "persist": {"display_name": "Persist"},
|
||||||
"persist_directory": {"display_name": "Persist Directory"},
|
"index_directory": {"display_name": "Index Directory"},
|
||||||
"code": {"show": False, "display_name": "Code"},
|
"code": {"show": False, "display_name": "Code"},
|
||||||
"documents": {"display_name": "Documents", "is_list": True},
|
"documents": {"display_name": "Documents", "is_list": True},
|
||||||
"embedding": {"display_name": "Embedding"},
|
"embedding": {
|
||||||
|
"display_name": "Embedding",
|
||||||
|
"info": "Embedding model to vectorize inputs (make sure to use same as index)",
|
||||||
|
},
|
||||||
"chroma_server_cors_allow_origins": {
|
"chroma_server_cors_allow_origins": {
|
||||||
"display_name": "Server CORS Allow Origins",
|
"display_name": "Server CORS Allow Origins",
|
||||||
"advanced": True,
|
"advanced": True,
|
||||||
|
|
@ -53,11 +60,9 @@ class ChromaSearchComponent(CustomComponent):
|
||||||
inputs: Text,
|
inputs: Text,
|
||||||
search_type: str,
|
search_type: str,
|
||||||
collection_name: str,
|
collection_name: str,
|
||||||
persist: bool,
|
|
||||||
embedding: Embeddings,
|
embedding: Embeddings,
|
||||||
chroma_server_ssl_enabled: bool,
|
chroma_server_ssl_enabled: bool,
|
||||||
persist_directory: Optional[str] = None,
|
index_directory: Optional[str] = None,
|
||||||
documents: Optional[List[Document]] = None,
|
|
||||||
chroma_server_cors_allow_origins: Optional[str] = None,
|
chroma_server_cors_allow_origins: Optional[str] = None,
|
||||||
chroma_server_host: Optional[str] = None,
|
chroma_server_host: Optional[str] = None,
|
||||||
chroma_server_port: Optional[int] = None,
|
chroma_server_port: Optional[int] = None,
|
||||||
|
|
@ -87,26 +92,20 @@ class ChromaSearchComponent(CustomComponent):
|
||||||
|
|
||||||
if chroma_server_host is not None:
|
if chroma_server_host is not None:
|
||||||
chroma_settings = chromadb.config.Settings(
|
chroma_settings = chromadb.config.Settings(
|
||||||
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins or None,
|
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins
|
||||||
|
or None,
|
||||||
chroma_server_host=chroma_server_host,
|
chroma_server_host=chroma_server_host,
|
||||||
chroma_server_port=chroma_server_port or None,
|
chroma_server_port=chroma_server_port or None,
|
||||||
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
||||||
chroma_server_ssl_enabled=chroma_server_ssl_enabled,
|
chroma_server_ssl_enabled=chroma_server_ssl_enabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
# If documents, then we need to create a Chroma instance using .from_documents
|
chroma = Chroma(
|
||||||
if documents is not None and embedding is not None:
|
embedding_function=embedding,
|
||||||
if len(documents) == 0:
|
collection_name=collection_name,
|
||||||
raise ValueError("If documents are provided, there must be at least one document.")
|
persist_directory=index_directory,
|
||||||
chroma = Chroma.from_documents(
|
client_settings=chroma_settings,
|
||||||
documents=documents, # type: ignore
|
)
|
||||||
persist_directory=persist_directory if persist else None,
|
|
||||||
collection_name=collection_name,
|
|
||||||
embedding=embedding,
|
|
||||||
client_settings=chroma_settings,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
chroma = Chroma(persist_directory=persist_directory, client_settings=chroma_settings)
|
|
||||||
|
|
||||||
# Validate the inputs
|
# Validate the inputs
|
||||||
docs = []
|
docs = []
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import pinecone # type: ignore
|
||||||
from langchain.schema import BaseRetriever
|
from langchain.schema import BaseRetriever
|
||||||
from langchain_community.vectorstores import VectorStore
|
from langchain_community.vectorstores import VectorStore
|
||||||
from langchain_community.vectorstores.pinecone import Pinecone
|
from langchain_community.vectorstores.pinecone import Pinecone
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import Document, Embeddings
|
from langflow.field_typing import Document, Embeddings
|
||||||
|
|
||||||
|
|
@ -31,11 +30,11 @@ class PineconeComponent(CustomComponent):
|
||||||
embedding: Embeddings,
|
embedding: Embeddings,
|
||||||
pinecone_env: str,
|
pinecone_env: str,
|
||||||
documents: List[Document],
|
documents: List[Document],
|
||||||
|
text_key: str = "text",
|
||||||
|
pool_threads: int = 4,
|
||||||
index_name: Optional[str] = None,
|
index_name: Optional[str] = None,
|
||||||
pinecone_api_key: Optional[str] = None,
|
pinecone_api_key: Optional[str] = None,
|
||||||
text_key: Optional[str] = "text",
|
|
||||||
namespace: Optional[str] = "default",
|
namespace: Optional[str] = "default",
|
||||||
pool_threads: Optional[int] = None,
|
|
||||||
) -> Union[VectorStore, Pinecone, BaseRetriever]:
|
) -> Union[VectorStore, Pinecone, BaseRetriever]:
|
||||||
if pinecone_api_key is None or pinecone_env is None:
|
if pinecone_api_key is None or pinecone_env is None:
|
||||||
raise ValueError("Pinecone API Key and Environment are required.")
|
raise ValueError("Pinecone API Key and Environment are required.")
|
||||||
|
|
@ -43,6 +42,8 @@ class PineconeComponent(CustomComponent):
|
||||||
raise ValueError("Pinecone API Key is required.")
|
raise ValueError("Pinecone API Key is required.")
|
||||||
|
|
||||||
pinecone.init(api_key=pinecone_api_key, environment=pinecone_env) # type: ignore
|
pinecone.init(api_key=pinecone_api_key, environment=pinecone_env) # type: ignore
|
||||||
|
if not index_name:
|
||||||
|
raise ValueError("Index Name is required.")
|
||||||
if documents:
|
if documents:
|
||||||
return Pinecone.from_documents(
|
return Pinecone.from_documents(
|
||||||
documents=documents,
|
documents=documents,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import List, Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from langchain.schema import BaseRetriever
|
from langchain.schema import BaseRetriever
|
||||||
from langchain_community.vectorstores import VectorStore
|
from langchain_community.vectorstores import VectorStore
|
||||||
|
|
@ -15,7 +15,7 @@ class QdrantComponent(CustomComponent):
|
||||||
return {
|
return {
|
||||||
"documents": {"display_name": "Documents"},
|
"documents": {"display_name": "Documents"},
|
||||||
"embedding": {"display_name": "Embedding"},
|
"embedding": {"display_name": "Embedding"},
|
||||||
"api_key": {"display_name": "API Key", "password": True},
|
"api_key": {"display_name": "API Key", "password": True, "advanced": True},
|
||||||
"collection_name": {"display_name": "Collection Name"},
|
"collection_name": {"display_name": "Collection Name"},
|
||||||
"content_payload_key": {"display_name": "Content Payload Key", "advanced": True},
|
"content_payload_key": {"display_name": "Content Payload Key", "advanced": True},
|
||||||
"distance_func": {"display_name": "Distance Function", "advanced": True},
|
"distance_func": {"display_name": "Distance Function", "advanced": True},
|
||||||
|
|
@ -36,41 +36,68 @@ class QdrantComponent(CustomComponent):
|
||||||
def build(
|
def build(
|
||||||
self,
|
self,
|
||||||
embedding: Embeddings,
|
embedding: Embeddings,
|
||||||
documents: List[Document],
|
collection_name: str,
|
||||||
|
documents: Optional[Document] = None,
|
||||||
api_key: Optional[str] = None,
|
api_key: Optional[str] = None,
|
||||||
collection_name: Optional[str] = None,
|
|
||||||
content_payload_key: str = "page_content",
|
content_payload_key: str = "page_content",
|
||||||
distance_func: str = "Cosine",
|
distance_func: str = "Cosine",
|
||||||
grpc_port: Optional[int] = 6334,
|
grpc_port: int = 6334,
|
||||||
host: Optional[str] = None,
|
|
||||||
https: bool = False,
|
https: bool = False,
|
||||||
location: str = ":memory:",
|
host: Optional[str] = None,
|
||||||
|
location: Optional[str] = None,
|
||||||
metadata_payload_key: str = "metadata",
|
metadata_payload_key: str = "metadata",
|
||||||
path: Optional[str] = None,
|
path: Optional[str] = None,
|
||||||
port: Optional[int] = 6333,
|
port: Optional[int] = 6333,
|
||||||
prefer_grpc: bool = False,
|
prefer_grpc: bool = False,
|
||||||
prefix: Optional[str] = None,
|
prefix: Optional[str] = None,
|
||||||
search_kwargs: Optional[NestedDict] = None,
|
search_kwargs: Optional[NestedDict] = None,
|
||||||
timeout: Optional[float] = None,
|
timeout: Optional[int] = None,
|
||||||
url: Optional[str] = None,
|
url: Optional[str] = None,
|
||||||
) -> Union[VectorStore, Qdrant, BaseRetriever]:
|
) -> Union[VectorStore, Qdrant, BaseRetriever]:
|
||||||
return Qdrant.from_documents(
|
if documents is None:
|
||||||
documents=documents,
|
from qdrant_client import QdrantClient
|
||||||
embedding=embedding,
|
|
||||||
api_key=api_key,
|
client = QdrantClient(
|
||||||
collection_name=collection_name,
|
location=location,
|
||||||
content_payload_key=content_payload_key,
|
url=host,
|
||||||
distance_func=distance_func,
|
port=port,
|
||||||
grpc_port=grpc_port,
|
grpc_port=grpc_port,
|
||||||
host=host,
|
https=https,
|
||||||
https=https,
|
prefix=prefix,
|
||||||
location=location,
|
timeout=timeout,
|
||||||
metadata_payload_key=metadata_payload_key,
|
prefer_grpc=prefer_grpc,
|
||||||
path=path,
|
metadata_payload_key=metadata_payload_key,
|
||||||
port=port,
|
content_payload_key=content_payload_key,
|
||||||
prefer_grpc=prefer_grpc,
|
api_key=api_key,
|
||||||
prefix=prefix,
|
collection_name=collection_name,
|
||||||
search_kwargs=search_kwargs,
|
host=host,
|
||||||
timeout=timeout,
|
path=path,
|
||||||
url=url,
|
)
|
||||||
)
|
vs = Qdrant(
|
||||||
|
client=client,
|
||||||
|
collection_name=collection_name,
|
||||||
|
embeddings=embedding,
|
||||||
|
)
|
||||||
|
return vs
|
||||||
|
else:
|
||||||
|
vs = Qdrant.from_documents(
|
||||||
|
documents=documents, # type: ignore
|
||||||
|
embedding=embedding,
|
||||||
|
api_key=api_key,
|
||||||
|
collection_name=collection_name,
|
||||||
|
content_payload_key=content_payload_key,
|
||||||
|
distance_func=distance_func,
|
||||||
|
grpc_port=grpc_port,
|
||||||
|
host=host,
|
||||||
|
https=https,
|
||||||
|
location=location,
|
||||||
|
metadata_payload_key=metadata_payload_key,
|
||||||
|
path=path,
|
||||||
|
port=port,
|
||||||
|
prefer_grpc=prefer_grpc,
|
||||||
|
prefix=prefix,
|
||||||
|
search_kwargs=search_kwargs,
|
||||||
|
timeout=timeout,
|
||||||
|
url=url,
|
||||||
|
)
|
||||||
|
return vs
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from langchain_community.vectorstores import VectorStore
|
||||||
from langchain_community.vectorstores.redis import Redis
|
from langchain_community.vectorstores.redis import Redis
|
||||||
from langchain_core.documents import Document
|
from langchain_core.documents import Document
|
||||||
from langchain_core.retrievers import BaseRetriever
|
from langchain_core.retrievers import BaseRetriever
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,6 +30,7 @@ class RedisComponent(CustomComponent):
|
||||||
"code": {"show": False, "display_name": "Code"},
|
"code": {"show": False, "display_name": "Code"},
|
||||||
"documents": {"display_name": "Documents", "is_list": True},
|
"documents": {"display_name": "Documents", "is_list": True},
|
||||||
"embedding": {"display_name": "Embedding"},
|
"embedding": {"display_name": "Embedding"},
|
||||||
|
"schema": {"display_name": "Schema", "file_types": [".yaml"]},
|
||||||
"redis_server_url": {
|
"redis_server_url": {
|
||||||
"display_name": "Redis Server Connection String",
|
"display_name": "Redis Server Connection String",
|
||||||
"advanced": False,
|
"advanced": False,
|
||||||
|
|
@ -43,6 +43,7 @@ class RedisComponent(CustomComponent):
|
||||||
embedding: Embeddings,
|
embedding: Embeddings,
|
||||||
redis_server_url: str,
|
redis_server_url: str,
|
||||||
redis_index_name: str,
|
redis_index_name: str,
|
||||||
|
schema: Optional[str] = None,
|
||||||
documents: Optional[Document] = None,
|
documents: Optional[Document] = None,
|
||||||
) -> Union[VectorStore, BaseRetriever]:
|
) -> Union[VectorStore, BaseRetriever]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,10 +59,12 @@ class RedisComponent(CustomComponent):
|
||||||
- VectorStore: The Vector Store object.
|
- VectorStore: The Vector Store object.
|
||||||
"""
|
"""
|
||||||
if documents is None:
|
if documents is None:
|
||||||
|
if schema is None:
|
||||||
|
raise ValueError("If no documents are provided, a schema must be provided.")
|
||||||
redis_vs = Redis.from_existing_index(
|
redis_vs = Redis.from_existing_index(
|
||||||
embedding=embedding,
|
embedding=embedding,
|
||||||
index_name=redis_index_name,
|
index_name=redis_index_name,
|
||||||
schema=None,
|
schema=schema,
|
||||||
key_prefix=None,
|
key_prefix=None,
|
||||||
redis_url=redis_server_url,
|
redis_url=redis_server_url,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ from typing import List, Optional, Union
|
||||||
from langchain_community.embeddings import FakeEmbeddings
|
from langchain_community.embeddings import FakeEmbeddings
|
||||||
from langchain_community.vectorstores.vectara import Vectara
|
from langchain_community.vectorstores.vectara import Vectara
|
||||||
from langchain_core.vectorstores import VectorStore
|
from langchain_core.vectorstores import VectorStore
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import BaseRetriever, Document
|
from langflow.field_typing import BaseRetriever, Document
|
||||||
|
|
||||||
|
|
@ -46,7 +45,7 @@ class VectaraComponent(CustomComponent):
|
||||||
|
|
||||||
if documents is not None:
|
if documents is not None:
|
||||||
return Vectara.from_documents(
|
return Vectara.from_documents(
|
||||||
documents=documents,
|
documents=documents, # type: ignore
|
||||||
embedding=FakeEmbeddings(size=768),
|
embedding=FakeEmbeddings(size=768),
|
||||||
vectara_customer_id=vectara_customer_id,
|
vectara_customer_id=vectara_customer_id,
|
||||||
vectara_corpus_id=vectara_corpus_id,
|
vectara_corpus_id=vectara_corpus_id,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from langchain_community.vectorstores import VectorStore
|
||||||
from langchain_community.vectorstores.pgvector import PGVector
|
from langchain_community.vectorstores.pgvector import PGVector
|
||||||
from langchain_core.documents import Document
|
from langchain_core.documents import Document
|
||||||
from langchain_core.retrievers import BaseRetriever
|
from langchain_core.retrievers import BaseRetriever
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,13 +62,13 @@ class PGVectorComponent(CustomComponent):
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
connection_string=pg_server_url,
|
connection_string=pg_server_url,
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
vector_store = PGVector.from_documents(
|
vector_store = PGVector.from_documents(
|
||||||
embedding=embedding,
|
embedding=embedding,
|
||||||
documents=documents,
|
documents=documents, # type: ignore
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
connection_string=pg_server_url,
|
connection_string=pg_server_url,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Failed to build PGVector: {e}")
|
raise RuntimeError(f"Failed to build PGVector: {e}")
|
||||||
return vector_store
|
return vector_store
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,12 @@ from langflow.graph.edge.base import ContractEdge
|
||||||
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
||||||
from langflow.graph.graph.utils import process_flow
|
from langflow.graph.graph.utils import process_flow
|
||||||
from langflow.graph.vertex.base import Vertex
|
from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.graph.vertex.types import ChatVertex, FileToolVertex, LLMVertex, ToolkitVertex
|
from langflow.graph.vertex.types import (
|
||||||
|
ChatVertex,
|
||||||
|
FileToolVertex,
|
||||||
|
LLMVertex,
|
||||||
|
ToolkitVertex,
|
||||||
|
)
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
@ -127,7 +132,9 @@ class Graph:
|
||||||
return
|
return
|
||||||
for vertex in self.vertices:
|
for vertex in self.vertices:
|
||||||
if not self._validate_vertex(vertex):
|
if not self._validate_vertex(vertex):
|
||||||
raise ValueError(f"{vertex.vertex_type} is not connected to any other components")
|
raise ValueError(
|
||||||
|
f"{vertex.vertex_type} is not connected to any other components"
|
||||||
|
)
|
||||||
|
|
||||||
def _validate_vertex(self, vertex: Vertex) -> bool:
|
def _validate_vertex(self, vertex: Vertex) -> bool:
|
||||||
"""Validates a vertex."""
|
"""Validates a vertex."""
|
||||||
|
|
@ -140,7 +147,11 @@ class Graph:
|
||||||
|
|
||||||
def get_vertex_edges(self, vertex_id: str) -> List[ContractEdge]:
|
def get_vertex_edges(self, vertex_id: str) -> List[ContractEdge]:
|
||||||
"""Returns a list of edges for a given vertex."""
|
"""Returns a list of edges for a given vertex."""
|
||||||
return [edge for edge in self.edges if edge.source_id == vertex_id or edge.target_id == vertex_id]
|
return [
|
||||||
|
edge
|
||||||
|
for edge in self.edges
|
||||||
|
if edge.source_id == vertex_id or edge.target_id == vertex_id
|
||||||
|
]
|
||||||
|
|
||||||
def get_vertices_with_target(self, vertex_id: str) -> List[Vertex]:
|
def get_vertices_with_target(self, vertex_id: str) -> List[Vertex]:
|
||||||
"""Returns the vertices connected to a vertex."""
|
"""Returns the vertices connected to a vertex."""
|
||||||
|
|
@ -178,7 +189,9 @@ class Graph:
|
||||||
def dfs(vertex):
|
def dfs(vertex):
|
||||||
if state[vertex] == 1:
|
if state[vertex] == 1:
|
||||||
# We have a cycle
|
# We have a cycle
|
||||||
raise ValueError("Graph contains a cycle, cannot perform topological sort")
|
raise ValueError(
|
||||||
|
"Graph contains a cycle, cannot perform topological sort"
|
||||||
|
)
|
||||||
if state[vertex] == 0:
|
if state[vertex] == 0:
|
||||||
state[vertex] = 1
|
state[vertex] = 1
|
||||||
for edge in vertex.edges:
|
for edge in vertex.edges:
|
||||||
|
|
@ -237,7 +250,9 @@ class Graph:
|
||||||
edges.append(ContractEdge(source, target, edge))
|
edges.append(ContractEdge(source, target, edge))
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -> Type[Vertex]:
|
def _get_vertex_class(
|
||||||
|
self, node_type: str, node_base_type: str, node_id: str
|
||||||
|
) -> Type[Vertex]:
|
||||||
"""Returns the node class based on the node type."""
|
"""Returns the node class based on the node type."""
|
||||||
# First we check for the node_base_type
|
# First we check for the node_base_type
|
||||||
node_name = node_id.split("-")[0]
|
node_name = node_id.split("-")[0]
|
||||||
|
|
@ -267,14 +282,18 @@ class Graph:
|
||||||
vertex_type: str = vertex_data["type"] # type: ignore
|
vertex_type: str = vertex_data["type"] # type: ignore
|
||||||
vertex_base_type: str = vertex_data["node"]["template"]["_type"] # type: ignore
|
vertex_base_type: str = vertex_data["node"]["template"]["_type"] # type: ignore
|
||||||
|
|
||||||
VertexClass = self._get_vertex_class(vertex_type, vertex_base_type, vertex_data["id"])
|
VertexClass = self._get_vertex_class(
|
||||||
|
vertex_type, vertex_base_type, vertex_data["id"]
|
||||||
|
)
|
||||||
vertex_instance = VertexClass(vertex, graph=self)
|
vertex_instance = VertexClass(vertex, graph=self)
|
||||||
vertex_instance.set_top_level(self.top_level_vertices)
|
vertex_instance.set_top_level(self.top_level_vertices)
|
||||||
vertices.append(vertex_instance)
|
vertices.append(vertex_instance)
|
||||||
|
|
||||||
return vertices
|
return vertices
|
||||||
|
|
||||||
def get_children_by_vertex_type(self, vertex: Vertex, vertex_type: str) -> List[Vertex]:
|
def get_children_by_vertex_type(
|
||||||
|
self, vertex: Vertex, vertex_type: str
|
||||||
|
) -> List[Vertex]:
|
||||||
"""Returns the children of a vertex based on the vertex type."""
|
"""Returns the children of a vertex based on the vertex type."""
|
||||||
children = []
|
children = []
|
||||||
vertex_types = [vertex.data["type"]]
|
vertex_types = [vertex.data["type"]]
|
||||||
|
|
@ -286,7 +305,9 @@ class Graph:
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
vertex_ids = [vertex.id for vertex in self.vertices]
|
vertex_ids = [vertex.id for vertex in self.vertices]
|
||||||
edges_repr = "\n".join([f"{edge.source_id} --> {edge.target_id}" for edge in self.edges])
|
edges_repr = "\n".join(
|
||||||
|
[f"{edge.source_id} --> {edge.target_id}" for edge in self.edges]
|
||||||
|
)
|
||||||
return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}"
|
return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}"
|
||||||
|
|
||||||
def layered_topological_sort(self):
|
def layered_topological_sort(self):
|
||||||
|
|
@ -299,7 +320,9 @@ class Graph:
|
||||||
in_degree[edge.target_id] += 1
|
in_degree[edge.target_id] += 1
|
||||||
|
|
||||||
# Queue for vertices with no incoming edges
|
# Queue for vertices with no incoming edges
|
||||||
queue = deque(vertex.id for vertex in self.vertices if in_degree[vertex.id] == 0)
|
queue = deque(
|
||||||
|
vertex.id for vertex in self.vertices if in_degree[vertex.id] == 0
|
||||||
|
)
|
||||||
layers = []
|
layers = []
|
||||||
|
|
||||||
current_layer = 0
|
current_layer = 0
|
||||||
|
|
@ -314,9 +337,40 @@ class Graph:
|
||||||
if in_degree[neighbor] == 0:
|
if in_degree[neighbor] == 0:
|
||||||
queue.append(neighbor)
|
queue.append(neighbor)
|
||||||
current_layer += 1 # Next layer
|
current_layer += 1 # Next layer
|
||||||
|
new_layers = self.refine_layers(graph, layers)
|
||||||
|
return new_layers
|
||||||
|
|
||||||
return layers
|
def refine_layers(self, graph, initial_layers):
|
||||||
return layers
|
# Map each vertex to its current layer
|
||||||
return layers
|
vertex_to_layer = {}
|
||||||
return layers
|
for layer_index, layer in enumerate(initial_layers):
|
||||||
return layers
|
for vertex in layer:
|
||||||
|
vertex_to_layer[vertex] = layer_index
|
||||||
|
|
||||||
|
# Build the adjacency list for reverse lookup (dependencies)
|
||||||
|
|
||||||
|
refined_layers = [[] for _ in initial_layers] # Start with empty layers
|
||||||
|
new_layer_index_map = defaultdict(
|
||||||
|
int
|
||||||
|
) # Map each vertex to its highest dependency layer
|
||||||
|
|
||||||
|
for vertex_id, deps in graph.items():
|
||||||
|
for dep in deps:
|
||||||
|
new_layer_index_map[vertex_id] = (
|
||||||
|
max(new_layer_index_map[vertex_id], vertex_to_layer[dep]) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for layer_index, layer in enumerate(initial_layers):
|
||||||
|
for vertex_id in layer:
|
||||||
|
# Place the vertex in the highest possible layer where its dependencies are met
|
||||||
|
new_layer_index = new_layer_index_map[vertex_id]
|
||||||
|
if new_layer_index > layer_index:
|
||||||
|
refined_layers[new_layer_index].append(vertex_id)
|
||||||
|
vertex_to_layer[vertex_id] = new_layer_index
|
||||||
|
else:
|
||||||
|
refined_layers[layer_index].append(vertex_id)
|
||||||
|
|
||||||
|
# Remove empty layers if any
|
||||||
|
refined_layers = [layer for layer in refined_layers if layer]
|
||||||
|
|
||||||
|
return refined_layers
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from typing import Any, ClassVar, Optional
|
||||||
import emoji
|
import emoji
|
||||||
from cachetools import TTLCache, cachedmethod
|
from cachetools import TTLCache, cachedmethod
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from langflow.interface.custom.code_parser import CodeParser
|
from langflow.interface.custom.code_parser import CodeParser
|
||||||
from langflow.interface.custom.eval import eval_custom_component_code
|
from langflow.interface.custom.eval import eval_custom_component_code
|
||||||
from langflow.utils import validate
|
from langflow.utils import validate
|
||||||
|
|
@ -21,7 +20,9 @@ class ComponentFunctionEntrypointNameNullError(HTTPException):
|
||||||
|
|
||||||
class Component:
|
class Component:
|
||||||
ERROR_CODE_NULL: ClassVar[str] = "Python code must be provided."
|
ERROR_CODE_NULL: ClassVar[str] = "Python code must be provided."
|
||||||
ERROR_FUNCTION_ENTRYPOINT_NAME_NULL: ClassVar[str] = "The name of the entrypoint function must be provided."
|
ERROR_FUNCTION_ENTRYPOINT_NAME_NULL: ClassVar[str] = (
|
||||||
|
"The name of the entrypoint function must be provided."
|
||||||
|
)
|
||||||
|
|
||||||
code: Optional[str] = None
|
code: Optional[str] = None
|
||||||
_function_entrypoint_name: str = "build"
|
_function_entrypoint_name: str = "build"
|
||||||
|
|
@ -36,10 +37,6 @@ class Component:
|
||||||
else:
|
else:
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
# Validate the emoji at the icon field
|
|
||||||
if self.icon:
|
|
||||||
self.icon = self.validate_icon(self.icon)
|
|
||||||
|
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
if key == "_user_id" and hasattr(self, "_user_id"):
|
if key == "_user_id" and hasattr(self, "_user_id"):
|
||||||
warnings.warn("user_id is immutable and cannot be changed.")
|
warnings.warn("user_id is immutable and cannot be changed.")
|
||||||
|
|
@ -68,8 +65,8 @@ class Component:
|
||||||
|
|
||||||
return validate.create_function(self.code, self._function_entrypoint_name)
|
return validate.create_function(self.code, self._function_entrypoint_name)
|
||||||
|
|
||||||
def getattr_return_str(self, component, value):
|
def getattr_return_str(self, value):
|
||||||
value = getattr(component, value)
|
|
||||||
return str(value) if value else ""
|
return str(value) if value else ""
|
||||||
|
|
||||||
def build_template_config(self) -> dict:
|
def build_template_config(self) -> dict:
|
||||||
|
|
@ -89,19 +86,22 @@ class Component:
|
||||||
|
|
||||||
for attribute, func in attributes_func_mapping.items():
|
for attribute, func in attributes_func_mapping.items():
|
||||||
if hasattr(component_instance, attribute):
|
if hasattr(component_instance, attribute):
|
||||||
template_config[attribute] = func(component=component_instance, value=attribute)
|
value = getattr(component_instance, attribute)
|
||||||
|
if value is not None:
|
||||||
|
template_config[attribute] = func(value=value)
|
||||||
|
|
||||||
return template_config
|
return template_config
|
||||||
|
|
||||||
def validate_icon(self, value: str, *args, **kwargs):
|
def validate_icon(self, value: str, *args, **kwargs):
|
||||||
# we are going to use the emoji library to validate the emoji
|
# we are going to use the emoji library to validate the emoji
|
||||||
# emojis can be defined using the :emoji_name: syntax
|
# emojis can be defined using the :emoji_name: syntax
|
||||||
if not value.startswith(":") or not value.endswith(":"):
|
if not value.startswith(":") or not value.endswith(":"):
|
||||||
raise ValueError("Invalid emoji. Please use the :emoji_name: syntax.")
|
warnings.warn("Invalid emoji. Please use the :emoji_name: syntax.")
|
||||||
|
return value
|
||||||
emoji_value = emoji.emojize(value, variant="emoji_type")
|
emoji_value = emoji.emojize(value, variant="emoji_type")
|
||||||
if value == emoji_value:
|
if value == emoji_value:
|
||||||
raise ValueError(f"Invalid emoji. {value} is not a valid emoji.")
|
warnings.warn(f"Invalid emoji. {value} is not a valid emoji.")
|
||||||
|
return value
|
||||||
return emoji_value
|
return emoji_value
|
||||||
|
|
||||||
def build(self, *args: Any, **kwargs: Any) -> Any:
|
def build(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import operator
|
import operator
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Callable, ClassVar, List, Optional, Union
|
from typing import Any, Callable, ClassVar, List, Optional, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from cachetools import TTLCache, cachedmethod
|
from cachetools import TTLCache, cachedmethod
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from langflow.interface.custom.code_parser.utils import (
|
from langflow.interface.custom.code_parser.utils import (
|
||||||
extract_inner_type_from_generic_alias,
|
extract_inner_type_from_generic_alias,
|
||||||
extract_union_types_from_generic_alias,
|
extract_union_types_from_generic_alias,
|
||||||
|
|
@ -13,7 +13,11 @@ from langflow.interface.custom.code_parser.utils import (
|
||||||
from langflow.interface.custom.custom_component.component import Component
|
from langflow.interface.custom.custom_component.component import Component
|
||||||
from langflow.services.database.models.flow import Flow
|
from langflow.services.database.models.flow import Flow
|
||||||
from langflow.services.database.utils import session_getter
|
from langflow.services.database.utils import session_getter
|
||||||
from langflow.services.deps import get_credential_service, get_db_service, get_storage_service
|
from langflow.services.deps import (
|
||||||
|
get_credential_service,
|
||||||
|
get_db_service,
|
||||||
|
get_storage_service,
|
||||||
|
)
|
||||||
from langflow.services.storage.service import StorageService
|
from langflow.services.storage.service import StorageService
|
||||||
from langflow.utils import validate
|
from langflow.utils import validate
|
||||||
|
|
||||||
|
|
@ -42,6 +46,16 @@ class CustomComponent(Component):
|
||||||
self.cache = TTLCache(maxsize=1024, ttl=60)
|
self.cache = TTLCache(maxsize=1024, ttl=60)
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_path(path: str) -> str:
|
||||||
|
"""Resolves the path to an absolute path."""
|
||||||
|
path_object = Path(path)
|
||||||
|
if path_object.parts[0] == "~":
|
||||||
|
path_object = path_object.expanduser()
|
||||||
|
elif path_object.is_relative_to("."):
|
||||||
|
path_object = path_object.resolve()
|
||||||
|
return str(path_object)
|
||||||
|
|
||||||
def get_full_path(self, path: str) -> str:
|
def get_full_path(self, path: str) -> str:
|
||||||
storage_svc: "StorageService" = get_storage_service()
|
storage_svc: "StorageService" = get_storage_service()
|
||||||
|
|
||||||
|
|
@ -78,7 +92,8 @@ class CustomComponent(Component):
|
||||||
detail={
|
detail={
|
||||||
"error": "Type hint Error",
|
"error": "Type hint Error",
|
||||||
"traceback": (
|
"traceback": (
|
||||||
"Prompt type is not supported in the build method." " Try using PromptTemplate instead."
|
"Prompt type is not supported in the build method."
|
||||||
|
" Try using PromptTemplate instead."
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -92,14 +107,20 @@ class CustomComponent(Component):
|
||||||
if not self.code:
|
if not self.code:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
component_classes = [cls for cls in self.tree["classes"] if self.code_class_base_inheritance in cls["bases"]]
|
component_classes = [
|
||||||
|
cls
|
||||||
|
for cls in self.tree["classes"]
|
||||||
|
if self.code_class_base_inheritance in cls["bases"]
|
||||||
|
]
|
||||||
if not component_classes:
|
if not component_classes:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# Assume the first Component class is the one we're interested in
|
# Assume the first Component class is the one we're interested in
|
||||||
component_class = component_classes[0]
|
component_class = component_classes[0]
|
||||||
build_methods = [
|
build_methods = [
|
||||||
method for method in component_class["methods"] if method["name"] == self.function_entrypoint_name
|
method
|
||||||
|
for method in component_class["methods"]
|
||||||
|
if method["name"] == self.function_entrypoint_name
|
||||||
]
|
]
|
||||||
|
|
||||||
return build_methods[0] if build_methods else {}
|
return build_methods[0] if build_methods else {}
|
||||||
|
|
@ -112,7 +133,10 @@ class CustomComponent(Component):
|
||||||
return_type = build_method["return_type"]
|
return_type = build_method["return_type"]
|
||||||
|
|
||||||
# If list or List is in the return type, then we remove it and return the inner type
|
# If list or List is in the return type, then we remove it and return the inner type
|
||||||
if hasattr(return_type, "__origin__") and return_type.__origin__ in [list, List]:
|
if hasattr(return_type, "__origin__") and return_type.__origin__ in [
|
||||||
|
list,
|
||||||
|
List,
|
||||||
|
]:
|
||||||
return_type = extract_inner_type_from_generic_alias(return_type)
|
return_type = extract_inner_type_from_generic_alias(return_type)
|
||||||
|
|
||||||
# If the return type is not a Union, then we just return it as a list
|
# If the return type is not a Union, then we just return it as a list
|
||||||
|
|
@ -153,7 +177,9 @@ class CustomComponent(Component):
|
||||||
# Retrieve and decrypt the credential by name for the current user
|
# Retrieve and decrypt the credential by name for the current user
|
||||||
db_service = get_db_service()
|
db_service = get_db_service()
|
||||||
with session_getter(db_service) as session:
|
with session_getter(db_service) as session:
|
||||||
return credential_service.get_credential(user_id=self._user_id or "", name=name, session=session)
|
return credential_service.get_credential(
|
||||||
|
user_id=self._user_id or "", name=name, session=session
|
||||||
|
)
|
||||||
|
|
||||||
return get_credential
|
return get_credential
|
||||||
|
|
||||||
|
|
@ -163,7 +189,9 @@ class CustomComponent(Component):
|
||||||
credential_service = get_credential_service()
|
credential_service = get_credential_service()
|
||||||
db_service = get_db_service()
|
db_service = get_db_service()
|
||||||
with session_getter(db_service) as session:
|
with session_getter(db_service) as session:
|
||||||
return credential_service.list_credentials(user_id=self._user_id, session=session)
|
return credential_service.list_credentials(
|
||||||
|
user_id=self._user_id, session=session
|
||||||
|
)
|
||||||
|
|
||||||
def index(self, value: int = 0):
|
def index(self, value: int = 0):
|
||||||
"""Returns a function that returns the value at the given index in the iterable."""
|
"""Returns a function that returns the value at the given index in the iterable."""
|
||||||
|
|
@ -214,7 +242,11 @@ class CustomComponent(Component):
|
||||||
if flow_id:
|
if flow_id:
|
||||||
flow = session.query(Flow).get(flow_id)
|
flow = session.query(Flow).get(flow_id)
|
||||||
elif flow_name:
|
elif flow_name:
|
||||||
flow = (session.query(Flow).filter(Flow.name == flow_name).filter(Flow.user_id == self.user_id)).first()
|
flow = (
|
||||||
|
session.query(Flow)
|
||||||
|
.filter(Flow.name == flow_name)
|
||||||
|
.filter(Flow.user_id == self.user_id)
|
||||||
|
).first()
|
||||||
else:
|
else:
|
||||||
raise ValueError("Either flow_name or flow_id must be provided")
|
raise ValueError("Either flow_name or flow_id must be provided")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ from typing import Any, Dict, List, Optional, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
from langflow.field_typing.range_spec import RangeSpec
|
from langflow.field_typing.range_spec import RangeSpec
|
||||||
from langflow.interface.custom.code_parser.utils import extract_inner_type
|
from langflow.interface.custom.code_parser.utils import extract_inner_type
|
||||||
from langflow.interface.custom.custom_component import CustomComponent
|
from langflow.interface.custom.custom_component import CustomComponent
|
||||||
|
|
@ -22,6 +20,7 @@ from langflow.template.field.base import TemplateField
|
||||||
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
|
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
|
||||||
from langflow.utils import validate
|
from langflow.utils import validate
|
||||||
from langflow.utils.util import get_base_classes
|
from langflow.utils.util import get_base_classes
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: List[str]):
|
def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: List[str]):
|
||||||
|
|
@ -151,9 +150,6 @@ def add_extra_fields(frontend_node, field_config, function_args):
|
||||||
if not function_args:
|
if not function_args:
|
||||||
return
|
return
|
||||||
|
|
||||||
# sort function_args which is a list of dicts
|
|
||||||
function_args.sort(key=lambda x: x["name"])
|
|
||||||
|
|
||||||
for extra_field in function_args:
|
for extra_field in function_args:
|
||||||
if "name" not in extra_field or extra_field["name"] == "self":
|
if "name" not in extra_field or extra_field["name"] == "self":
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,12 @@ async def instantiate_custom_component(node_type, class_object, params, user_id)
|
||||||
|
|
||||||
if is_async:
|
if is_async:
|
||||||
# Await the build method directly if it's async
|
# Await the build method directly if it's async
|
||||||
built_object = await custom_component.build(**params_copy)
|
build_result = await custom_component.build(**params_copy)
|
||||||
else:
|
else:
|
||||||
# Call the build method directly if it's sync
|
# Call the build method directly if it's sync
|
||||||
built_object = custom_component.build(**params_copy)
|
build_result = custom_component.build(**params_copy)
|
||||||
|
|
||||||
return built_object, {"repr": custom_component.custom_repr()}
|
return build_result, {"repr": custom_component.custom_repr()}
|
||||||
|
|
||||||
|
|
||||||
def instantiate_wrapper(node_type, class_object, params):
|
def instantiate_wrapper(node_type, class_object, params):
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@ from typing import TYPE_CHECKING, List, Union
|
||||||
|
|
||||||
from langchain.agents.agent import AgentExecutor
|
from langchain.agents.agent import AgentExecutor
|
||||||
from langchain.callbacks.base import BaseCallbackHandler
|
from langchain.callbacks.base import BaseCallbackHandler
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.api.v1.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler
|
from langflow.api.v1.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler
|
||||||
from langflow.processing.process import fix_memory_inputs, format_actions
|
from langflow.processing.process import fix_memory_inputs, format_actions
|
||||||
from langflow.services.deps import get_plugins_service
|
from langflow.services.deps import get_plugins_service
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langfuse.callback import CallbackHandler # type: ignore
|
from langfuse.callback import CallbackHandler # type: ignore
|
||||||
|
|
@ -28,13 +29,12 @@ def setup_callbacks(sync, trace_id, **kwargs):
|
||||||
|
|
||||||
def get_langfuse_callback(trace_id):
|
def get_langfuse_callback(trace_id):
|
||||||
from langflow.services.deps import get_plugins_service
|
from langflow.services.deps import get_plugins_service
|
||||||
from langfuse.callback import CreateTrace
|
|
||||||
|
|
||||||
logger.debug("Initializing langfuse callback")
|
logger.debug("Initializing langfuse callback")
|
||||||
if langfuse := get_plugins_service().get("langfuse"):
|
if langfuse := get_plugins_service().get("langfuse"):
|
||||||
logger.debug("Langfuse credentials found")
|
logger.debug("Langfuse credentials found")
|
||||||
try:
|
try:
|
||||||
trace = langfuse.trace(CreateTrace(name="langflow-" + trace_id, id=trace_id))
|
trace = langfuse.trace(name="langflow-" + trace_id, id=trace_id)
|
||||||
return trace.getNewHandler()
|
return trace.getNewHandler()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"Error initializing langfuse callback: {exc}")
|
logger.error(f"Error initializing langfuse callback: {exc}")
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,13 @@ class LangfusePlugin(CallbackPlugin):
|
||||||
def get_callback(self, _id: Optional[str] = None):
|
def get_callback(self, _id: Optional[str] = None):
|
||||||
if _id is None:
|
if _id is None:
|
||||||
_id = "default"
|
_id = "default"
|
||||||
from langfuse.callback import CreateTrace # type: ignore
|
|
||||||
|
|
||||||
logger.debug("Initializing langfuse callback")
|
logger.debug("Initializing langfuse callback")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
langfuse_instance = self.get()
|
langfuse_instance = self.get()
|
||||||
if langfuse_instance is not None and hasattr(langfuse_instance, "trace"):
|
if langfuse_instance is not None and hasattr(langfuse_instance, "trace"):
|
||||||
trace = langfuse_instance.trace(CreateTrace(name="langflow-" + _id, id=_id))
|
trace = langfuse_instance.trace(name="langflow-" + _id, id=_id)
|
||||||
if trace:
|
if trace:
|
||||||
return trace.getNewHandler()
|
return trace.getNewHandler()
|
||||||
|
|
||||||
|
|
|
||||||
10
src/frontend/package-lock.json
generated
10
src/frontend/package-lock.json
generated
|
|
@ -45,7 +45,7 @@
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"esbuild": "^0.17.19",
|
"esbuild": "^0.17.19",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.233.0",
|
"lucide-react": "^0.331.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.5.1"
|
"vite": "^4.5.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
|
|
@ -7220,9 +7220,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lucide-react": {
|
"node_modules/lucide-react": {
|
||||||
"version": "0.233.0",
|
"version": "0.331.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.233.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.331.0.tgz",
|
||||||
"integrity": "sha512-r0jMHF0vPDq2wBbZ0B3rtIcBjDyWDKpHu+vAjD2OHn2WLUr3HN5IHovtO0EMgQXuSI7YrMZbjsEZWC2uBHr8nQ==",
|
"integrity": "sha512-CHFJ0ve9vaZ7bB2VRAl27SlX1ELh6pfNC0jS96qGpPEEzLkLDGq4pDBFU8RhOoRMqsjXqTzLm9U6bZ1OcIHq7Q==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"esbuild": "^0.17.19",
|
"esbuild": "^0.17.19",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.233.0",
|
"lucide-react": "^0.331.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
|
|
@ -121,6 +121,6 @@
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.5.1"
|
"vite": "^4.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export default function App() {
|
||||||
);
|
);
|
||||||
const loading = useAlertStore((state) => state.loading);
|
const loading = useAlertStore((state) => state.loading);
|
||||||
const [fetchError, setFetchError] = useState(false);
|
const [fetchError, setFetchError] = useState(false);
|
||||||
|
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||||
|
|
||||||
const removeAlert = (id: string) => {
|
const removeAlert = (id: string) => {
|
||||||
removeFromTempNotificationList(id);
|
removeFromTempNotificationList(id);
|
||||||
|
|
@ -86,7 +87,7 @@ export default function App() {
|
||||||
description={FETCH_ERROR_DESCRIPION}
|
description={FETCH_ERROR_DESCRIPION}
|
||||||
message={FETCH_ERROR_MESSAGE}
|
message={FETCH_ERROR_MESSAGE}
|
||||||
></FetchErrorComponent>
|
></FetchErrorComponent>
|
||||||
) : loading ? (
|
) : isLoading ? (
|
||||||
<div className="loading-page-panel">
|
<div className="loading-page-panel">
|
||||||
<LoadingComponent remSize={50} />
|
<LoadingComponent remSize={50} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { NodeToolbar } from "reactflow";
|
import { NodeToolbar } from "reactflow";
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import Tooltip from "../../components/TooltipComponent";
|
import Tooltip from "../../components/TooltipComponent";
|
||||||
|
|
@ -112,6 +112,39 @@ export default function GenericNode({
|
||||||
|
|
||||||
const nameEditable = data.node?.flow || data.type === "CustomComponent";
|
const nameEditable = data.node?.flow || data.type === "CustomComponent";
|
||||||
|
|
||||||
|
const emojiRegex = /\p{Emoji}/u;
|
||||||
|
const isEmoji = emojiRegex.test(data?.node?.icon!);
|
||||||
|
|
||||||
|
const iconNodeRender = useCallback(() => {
|
||||||
|
const iconElement = data?.node?.icon;
|
||||||
|
const iconColor = nodeColors[types[data.type]];
|
||||||
|
const iconName =
|
||||||
|
iconElement || (data.node?.flow ? "group_components" : name);
|
||||||
|
const iconClassName = `generic-node-icon ${
|
||||||
|
!showNode ? "absolute inset-x-6 h-12 w-12" : ""
|
||||||
|
}`;
|
||||||
|
|
||||||
|
if (iconElement && isEmoji) {
|
||||||
|
return nodeIconFragment(iconElement);
|
||||||
|
} else {
|
||||||
|
return checkNodeIconFragment(iconColor, iconName, iconClassName);
|
||||||
|
}
|
||||||
|
}, [data, isEmoji, name, showNode]);
|
||||||
|
|
||||||
|
const nodeIconFragment = (icon) => {
|
||||||
|
return <span className="text-lg">{icon}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNodeIconFragment = (iconColor, iconName, iconClassName) => {
|
||||||
|
return (
|
||||||
|
<IconComponent
|
||||||
|
name={iconName}
|
||||||
|
className={iconClassName}
|
||||||
|
iconColor={iconColor}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NodeToolbar>
|
<NodeToolbar>
|
||||||
|
|
@ -164,19 +197,7 @@ export default function GenericNode({
|
||||||
(!showNode && "justify-center")
|
(!showNode && "justify-center")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{data?.node?.icon ? (
|
{iconNodeRender()}
|
||||||
<span className="text-lg">{data?.node?.icon}</span>
|
|
||||||
) : (
|
|
||||||
<IconComponent
|
|
||||||
name={data.node?.flow ? "group_components" : name}
|
|
||||||
className={
|
|
||||||
"generic-node-icon " +
|
|
||||||
(!showNode ? "absolute inset-x-6 h-12 w-12" : "")
|
|
||||||
}
|
|
||||||
iconColor={`${nodeColors[types[data.type]]}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showNode && (
|
{showNode && (
|
||||||
<div className="generic-node-tooltip-div">
|
<div className="generic-node-tooltip-div">
|
||||||
{nameEditable && inputName ? (
|
{nameEditable && inputName ? (
|
||||||
|
|
@ -370,7 +391,7 @@ export default function GenericNode({
|
||||||
<span className="flex">
|
<span className="flex">
|
||||||
Build{" "}
|
Build{" "}
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Zap"
|
name="Play"
|
||||||
className=" h-5 fill-build-trigger stroke-build-trigger stroke-1"
|
className=" h-5 fill-build-trigger stroke-build-trigger stroke-1"
|
||||||
/>{" "}
|
/>{" "}
|
||||||
flow to validate status.
|
flow to validate status.
|
||||||
|
|
@ -390,7 +411,7 @@ export default function GenericNode({
|
||||||
>
|
>
|
||||||
<div className="generic-node-status-position flex items-center justify-center">
|
<div className="generic-node-status-position flex items-center justify-center">
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Zap"
|
name="Play"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
validationStatus && validationStatus.valid
|
validationStatus && validationStatus.valid
|
||||||
? "green-status"
|
? "green-status"
|
||||||
|
|
@ -399,7 +420,7 @@ export default function GenericNode({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Zap"
|
name="Play"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
validationStatus && !validationStatus.valid
|
validationStatus && !validationStatus.valid
|
||||||
? "red-status"
|
? "red-status"
|
||||||
|
|
@ -408,7 +429,7 @@ export default function GenericNode({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Zap"
|
name="Play"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
!validationStatus || isBuilding
|
!validationStatus || isBuilding
|
||||||
? "yellow-status"
|
? "yellow-status"
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const buttonVariants = cva(
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||||
outline:
|
outline:
|
||||||
"border border-input hover:bg-accent hover:text-accent-foreground",
|
"border border-input hover:bg-input hover:text-accent-foreground",
|
||||||
primary:
|
primary:
|
||||||
"border bg-background text-secondary-foreground hover:bg-secondary-foreground/5 dark:hover:bg-background/10 hover:shadow-sm",
|
"border bg-background text-secondary-foreground hover:bg-secondary-foreground/5 dark:hover:bg-background/10 hover:shadow-sm",
|
||||||
secondary:
|
secondary:
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,6 @@ export default function ComponentsComponent({
|
||||||
setPageSize(10);
|
setPageSize(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
setLoadingScreen(false);
|
|
||||||
}, 600);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardsWrapComponent
|
<CardsWrapComponent
|
||||||
onFileDrop={onFileDrop}
|
onFileDrop={onFileDrop}
|
||||||
|
|
@ -107,7 +101,7 @@ export default function ComponentsComponent({
|
||||||
>
|
>
|
||||||
<div className="flex h-full w-full flex-col justify-between">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<div className="flex w-full flex-col gap-4">
|
<div className="flex w-full flex-col gap-4">
|
||||||
{!loadingScreen && data.length === 0 ? (
|
{!isLoading && data.length === 0 ? (
|
||||||
<div className="mt-6 flex w-full items-center justify-center text-center">
|
<div className="mt-6 flex w-full items-center justify-center text-center">
|
||||||
<div className="flex-max-width h-full flex-col">
|
<div className="flex-max-width h-full flex-col">
|
||||||
<div className="flex w-full flex-col gap-4">
|
<div className="flex w-full flex-col gap-4">
|
||||||
|
|
@ -136,7 +130,7 @@ export default function ComponentsComponent({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-2">
|
<div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||||
{loadingScreen === false && data?.length > 0 ? (
|
{isLoading === false && data?.length > 0 ? (
|
||||||
data?.map((item, idx) => (
|
data?.map((item, idx) => (
|
||||||
<CollectionCardComponent
|
<CollectionCardComponent
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
|
|
@ -185,7 +179,7 @@ export default function ComponentsComponent({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!loadingScreen && data.length > 0 && (
|
{!isLoading && data.length > 0 && (
|
||||||
<div className="relative py-6">
|
<div className="relative py-6">
|
||||||
<PaginatorComponent
|
<PaginatorComponent
|
||||||
storeComponent={true}
|
storeComponent={true}
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
if (dbData) {
|
if (dbData) {
|
||||||
const { data, flows } = processFlows(dbData, false);
|
const { data, flows } = processFlows(dbData, false);
|
||||||
get().setFlows(flows);
|
get().setFlows(flows);
|
||||||
set({ isLoading: false });
|
|
||||||
useTypesStore.setState((state) => ({
|
useTypesStore.setState((state) => ({
|
||||||
data: { ...state.data, ["saved_components"]: data },
|
data: { ...state.data, ["saved_components"]: data },
|
||||||
}));
|
}));
|
||||||
|
set({ isLoading: false });
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { APIDataType } from "../types/api";
|
||||||
import { TypesStoreType } from "../types/zustand/types";
|
import { TypesStoreType } from "../types/zustand/types";
|
||||||
import { templatesGenerator, typesGenerator } from "../utils/reactflowUtils";
|
import { templatesGenerator, typesGenerator } from "../utils/reactflowUtils";
|
||||||
import useAlertStore from "./alertStore";
|
import useAlertStore from "./alertStore";
|
||||||
|
import useFlowsManagerStore from "./flowsManagerStore";
|
||||||
|
|
||||||
export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
types: {},
|
types: {},
|
||||||
|
|
@ -11,6 +12,8 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
data: {},
|
data: {},
|
||||||
getTypes: () => {
|
getTypes: () => {
|
||||||
return new Promise<void>(async (resolve, reject) => {
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
const setLoading = useFlowsManagerStore.getState().setIsLoading;
|
||||||
|
setLoading(true);
|
||||||
getAll()
|
getAll()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
|
|
@ -20,6 +23,7 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
data: { ...old.data, ...data },
|
data: { ...old.data, ...data },
|
||||||
templates: templatesGenerator(data),
|
templates: templatesGenerator(data),
|
||||||
}));
|
}));
|
||||||
|
setLoading(false)
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,11 @@ export async function buildVertices({
|
||||||
for (let i = 0; i < verticesOrder.length; i += 1) {
|
for (let i = 0; i < verticesOrder.length; i += 1) {
|
||||||
const innerArray = verticesOrder[i];
|
const innerArray = verticesOrder[i];
|
||||||
const idIndex = innerArray.indexOf(nodeId);
|
const idIndex = innerArray.indexOf(nodeId);
|
||||||
|
|
||||||
if (idIndex !== -1) {
|
if (idIndex !== -1) {
|
||||||
// If the targetId is found in the inner array, cut the array before the id
|
// If there's a nodeId, we want to run just that component and not the entire layer
|
||||||
vertices.push(innerArray.slice(0, idIndex + 1));
|
// because a layer contains dependencies for the next layer
|
||||||
|
// and we are stopping at the layer that contains the nodeId
|
||||||
|
vertices.push([innerArray[idIndex]]);
|
||||||
break; // Stop searching after finding the first occurrence
|
break; // Stop searching after finding the first occurrence
|
||||||
}
|
}
|
||||||
// If the targetId is not found, include the entire inner array
|
// If the targetId is not found, include the entire inner array
|
||||||
|
|
@ -38,7 +39,7 @@ export async function buildVertices({
|
||||||
} else {
|
} else {
|
||||||
vertices = verticesOrder;
|
vertices = verticesOrder;
|
||||||
}
|
}
|
||||||
|
console.log("Vertices: ", vertices);
|
||||||
const buildResults: Array<boolean> = [];
|
const buildResults: Array<boolean> = [];
|
||||||
for (let i = 0; i < vertices.length; i += 1) {
|
for (let i = 0; i < vertices.length; i += 1) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import {
|
||||||
FileSearch,
|
FileSearch,
|
||||||
FileSearch2,
|
FileSearch2,
|
||||||
FileText,
|
FileText,
|
||||||
|
Cable,
|
||||||
FileUp,
|
FileUp,
|
||||||
Fingerprint,
|
Fingerprint,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
|
|
@ -70,6 +71,7 @@ import {
|
||||||
Paperclip,
|
Paperclip,
|
||||||
Pencil,
|
Pencil,
|
||||||
Pin,
|
Pin,
|
||||||
|
Play,
|
||||||
Plus,
|
Plus,
|
||||||
Redo,
|
Redo,
|
||||||
RefreshCcw,
|
RefreshCcw,
|
||||||
|
|
@ -237,6 +239,7 @@ export const nodeNames: { [char: string]: string } = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const nodeIconsLucide: iconsType = {
|
export const nodeIconsLucide: iconsType = {
|
||||||
|
Play,
|
||||||
Vectara: VectaraIcon,
|
Vectara: VectaraIcon,
|
||||||
ArrowUpToLine: ArrowUpToLine,
|
ArrowUpToLine: ArrowUpToLine,
|
||||||
Chroma: ChromaIcon,
|
Chroma: ChromaIcon,
|
||||||
|
|
@ -394,7 +397,7 @@ export const nodeIconsLucide: iconsType = {
|
||||||
Combine,
|
Combine,
|
||||||
TerminalIcon,
|
TerminalIcon,
|
||||||
Repeat,
|
Repeat,
|
||||||
io: ArrowDownUp,
|
io: Cable,
|
||||||
ScreenShare,
|
ScreenShare,
|
||||||
Code,
|
Code,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,16 @@ export function groupByFamily(
|
||||||
})))
|
})))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
console.log(flow);
|
||||||
|
|
||||||
if (flow) {
|
if (flow) {
|
||||||
|
// se existir o flow
|
||||||
for (const node of flow) {
|
for (const node of flow) {
|
||||||
|
// para cada node do flow
|
||||||
|
if (node!.data!.node!.flow) break; // não faz nada se o node for um group
|
||||||
const nodeData = node.data;
|
const nodeData = node.data;
|
||||||
|
|
||||||
const foundNode = checkedNodes.get(nodeData.type);
|
const foundNode = checkedNodes.get(nodeData.type); // verifica se o tipo do node já foi checado
|
||||||
checkedNodes.set(nodeData.type, {
|
checkedNodes.set(nodeData.type, {
|
||||||
hasBaseClassInTemplate:
|
hasBaseClassInTemplate:
|
||||||
foundNode?.hasBaseClassInTemplate ||
|
foundNode?.hasBaseClassInTemplate ||
|
||||||
|
|
@ -153,7 +157,7 @@ export function groupByFamily(
|
||||||
foundNode?.hasBaseClassInBaseClasses ||
|
foundNode?.hasBaseClassInBaseClasses ||
|
||||||
nodeData.node!.base_classes.some((baseClass) =>
|
nodeData.node!.base_classes.some((baseClass) =>
|
||||||
baseClassesSet.has(baseClass)
|
baseClassesSet.has(baseClass)
|
||||||
),
|
), //seta como anterior ou verifica se o node tem base class
|
||||||
displayName: nodeData.node?.display_name,
|
displayName: nodeData.node?.display_name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,35 +611,36 @@ def test_async_task_processing(distributed_client, flow, created_api_key):
|
||||||
assert "Gabriel" in task_status_json["result"]["text"], task_status_json["result"]
|
assert "Gabriel" in task_status_json["result"]["text"], task_status_json["result"]
|
||||||
|
|
||||||
|
|
||||||
|
# ! Deactivating this until updating the test
|
||||||
# Test function without loop
|
# Test function without loop
|
||||||
@pytest.mark.async_test
|
# @pytest.mark.async_test
|
||||||
def test_async_task_processing_vector_store(client, added_vector_store, created_api_key):
|
# def test_async_task_processing_vector_store(client, added_vector_store, created_api_key):
|
||||||
headers = {"x-api-key": created_api_key.api_key}
|
# headers = {"x-api-key": created_api_key.api_key}
|
||||||
post_data = {"inputs": {"input": "How do I upload examples?"}}
|
# post_data = {"inputs": {"input": "How do I upload examples?"}}
|
||||||
|
|
||||||
# Run the /api/v1/process/{flow_id} endpoint with sync=False
|
# # Run the /api/v1/process/{flow_id} endpoint with sync=False
|
||||||
response = client.post(
|
# response = client.post(
|
||||||
f"api/v1/process/{added_vector_store.get('id')}",
|
# f"api/v1/process/{added_vector_store.get('id')}",
|
||||||
headers=headers,
|
# headers=headers,
|
||||||
json={**post_data, "sync": False},
|
# json={**post_data, "sync": False},
|
||||||
)
|
# )
|
||||||
assert response.status_code == 200, response.json()
|
# assert response.status_code == 200, response.json()
|
||||||
assert "result" in response.json()
|
# assert "result" in response.json()
|
||||||
assert "FAILURE" not in response.json()["result"]
|
# assert "FAILURE" not in response.json()["result"]
|
||||||
|
|
||||||
# Extract the task ID from the response
|
# # Extract the task ID from the response
|
||||||
task = response.json().get("task")
|
# task = response.json().get("task")
|
||||||
task_id = task.get("id")
|
# task_id = task.get("id")
|
||||||
task_href = task.get("href")
|
# task_href = task.get("href")
|
||||||
assert task_id is not None
|
# assert task_id is not None
|
||||||
assert task_href is not None
|
# assert task_href is not None
|
||||||
assert task_href == f"api/v1/task/{task_id}"
|
# assert task_href == f"api/v1/task/{task_id}"
|
||||||
|
|
||||||
# Polling the task status using the helper function
|
# # Polling the task status using the helper function
|
||||||
task_status_json = poll_task_status(client, headers, task_href)
|
# task_status_json = poll_task_status(client, headers, task_href)
|
||||||
assert task_status_json is not None, "Task did not complete in time"
|
# assert task_status_json is not None, "Task did not complete in time"
|
||||||
|
|
||||||
# Validate that the task completed successfully and the result is as expected
|
# # Validate that the task completed successfully and the result is as expected
|
||||||
assert "result" in task_status_json, task_status_json
|
# assert "result" in task_status_json, task_status_json
|
||||||
assert "output" in task_status_json["result"], task_status_json["result"]
|
# assert "output" in task_status_json["result"], task_status_json["result"]
|
||||||
assert "Langflow" in task_status_json["result"]["output"], task_status_json["result"]
|
# assert "Langflow" in task_status_json["result"]["output"], task_status_json["result"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue