Merge remote-tracking branch 'origin/dev' into newGroupNode

This commit is contained in:
anovazzi1 2023-09-10 19:54:28 -03:00
commit db4bb0c822
82 changed files with 879 additions and 456 deletions

0
.githooks/pre-commit Normal file → Executable file
View file

View file

@ -27,7 +27,7 @@ format:
cd src/frontend && npm run format
lint:
poetry run mypy --exclude .venv .
poetry run mypy src/backend/langflow
poetry run black . --check
poetry run ruff . --fix

234
poetry.lock generated
View file

@ -165,13 +165,13 @@ tz = ["python-dateutil"]
[[package]]
name = "anthropic"
version = "0.3.10"
version = "0.3.11"
description = "Client library for the anthropic API"
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "anthropic-0.3.10-py3-none-any.whl", hash = "sha256:95cd73168296a4f91a5899a28660991e044322cf94442d07b99901d4ca74acd6"},
{file = "anthropic-0.3.10.tar.gz", hash = "sha256:d1f66efc541fbff0ecfd37fd4d3690f9daaa748fc42d9ded5863a10815a5d97b"},
{file = "anthropic-0.3.11-py3-none-any.whl", hash = "sha256:5c81105cd9ee7388bff3fdb739aaddedc83bbae9b95d51c2d50c13b1ad106138"},
{file = "anthropic-0.3.11.tar.gz", hash = "sha256:2e0fa5351c9b368cbed0bbd7217deaa9409b82b56afaf244e2196e99eb4fe20e"},
]
[package.dependencies]
@ -969,13 +969,13 @@ test-randomorder = ["pytest-randomly"]
[[package]]
name = "ctransformers"
version = "0.2.24"
version = "0.2.25"
description = "Python bindings for the Transformer models implemented in C/C++ using GGML library."
optional = true
python-versions = "*"
files = [
{file = "ctransformers-0.2.24-py3-none-any.whl", hash = "sha256:fa2ad7a38726c3ad6e57d1aff696f6e89fe3c0de5df2109b579cb6bc6c2ef599"},
{file = "ctransformers-0.2.24.tar.gz", hash = "sha256:bb463204f557d00d533e1dc50346e0b57870cea68965ec135d3fa8db1c76ed2e"},
{file = "ctransformers-0.2.25-py3-none-any.whl", hash = "sha256:a97405373c61c8d8f0279b88f96543caec638773a4fe82ff2ed8d993844830f1"},
{file = "ctransformers-0.2.25.tar.gz", hash = "sha256:59d654a4be8a51f0c5b7976d8d39a1e410f5e808f8cdabad02da4957001053d8"},
]
[package.dependencies]
@ -1449,18 +1449,21 @@ zstandard = ["zstandard"]
[[package]]
name = "filelock"
version = "3.12.2"
version = "3.12.3"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
{file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
{file = "filelock-3.12.3-py3-none-any.whl", hash = "sha256:f067e40ccc40f2b48395a80fcbd4728262fab54e232e090a4063ab804179efeb"},
{file = "filelock-3.12.3.tar.gz", hash = "sha256:0ecc1dd2ec4672a10c8550a8182f1bd0c0a5088470ecd5a125e45f49472fac3d"},
]
[package.dependencies]
typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.11\""}
[package.extras]
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"]
[[package]]
name = "filetype"
@ -2848,13 +2851,13 @@ files = [
[[package]]
name = "jupyter-client"
version = "8.3.0"
version = "8.3.1"
description = "Jupyter protocol implementation and client libraries"
optional = false
python-versions = ">=3.8"
files = [
{file = "jupyter_client-8.3.0-py3-none-any.whl", hash = "sha256:7441af0c0672edc5d28035e92ba5e32fadcfa8a4e608a434c228836a89df6158"},
{file = "jupyter_client-8.3.0.tar.gz", hash = "sha256:3af69921fe99617be1670399a0b857ad67275eefcfa291e2c81a160b7b650f5f"},
{file = "jupyter_client-8.3.1-py3-none-any.whl", hash = "sha256:5eb9f55eb0650e81de6b7e34308d8b92d04fe4ec41cd8193a913979e33d8e1a5"},
{file = "jupyter_client-8.3.1.tar.gz", hash = "sha256:60294b2d5b869356c893f57b1a877ea6510d60d45cf4b38057f1672d85699ac9"},
]
[package.dependencies]
@ -2991,13 +2994,13 @@ test = ["psutil", "pytest", "pytest-asyncio"]
[[package]]
name = "langsmith"
version = "0.0.27"
version = "0.0.29"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langsmith-0.0.27-py3-none-any.whl", hash = "sha256:f61b07f093ba377b9af53c3d6f68fd1245f8f28605d4fc88433208aca93a5a23"},
{file = "langsmith-0.0.27.tar.gz", hash = "sha256:c4df680ee8bf88d37f56ba196048341847c48b50ae561719c5542ef6488170e5"},
{file = "langsmith-0.0.29-py3-none-any.whl", hash = "sha256:1b8fa032bf1671d80fed027545aa822ef8f39fec0381b799a9c511120e8e8258"},
{file = "langsmith-0.0.29.tar.gz", hash = "sha256:d389a0199937cb219d7cb90d4adbed7f3902818b22b604bb1ced5b74c571cfe5"},
]
[package.dependencies]
@ -3026,12 +3029,12 @@ test = ["coverage", "pytest", "pytest-cov"]
[[package]]
name = "llama-cpp-python"
version = "0.1.81"
version = "0.1.83"
description = "A Python wrapper for llama.cpp"
optional = true
python-versions = ">=3.7"
files = [
{file = "llama_cpp_python-0.1.81.tar.gz", hash = "sha256:8b8fa42e41c6334efe056571b5f19056ffd9776b94ee152530e1fb9fe81deda2"},
{file = "llama_cpp_python-0.1.83.tar.gz", hash = "sha256:9f40656e46a85a3c3427790246e03490bb90202c37cb99732a095ffcb99efe54"},
]
[package.dependencies]
@ -3044,13 +3047,13 @@ server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (
[[package]]
name = "loguru"
version = "0.7.0"
version = "0.7.1"
description = "Python logging made (stupidly) simple"
optional = false
python-versions = ">=3.5"
files = [
{file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"},
{file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"},
{file = "loguru-0.7.1-py3-none-any.whl", hash = "sha256:046bf970cb3cad77a28d607cbf042ac25a407db987a1e801c7f7e692469982f9"},
{file = "loguru-0.7.1.tar.gz", hash = "sha256:7ba2a7d81b79a412b0ded69bd921e012335e80fd39937a633570f273a343579e"},
]
[package.dependencies]
@ -3058,7 +3061,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"]
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "pre-commit (==3.3.1)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
[[package]]
name = "lxml"
@ -3812,13 +3815,13 @@ sympy = "*"
[[package]]
name = "openai"
version = "0.27.9"
version = "0.27.10"
description = "Python client library for the OpenAI API"
optional = false
python-versions = ">=3.7.1"
files = [
{file = "openai-0.27.9-py3-none-any.whl", hash = "sha256:6a3cf8e276d1a6262b50562fbc0cba7967cfebb78ed827d375986b48fdad6475"},
{file = "openai-0.27.9.tar.gz", hash = "sha256:b687761c82f5ebb6f61efc791b2083d2d068277b94802d4d1369efe39851813d"},
{file = "openai-0.27.10-py3-none-any.whl", hash = "sha256:beabd1757e3286fa166dde3b70ebb5ad8081af046876b47c14c41e203ed22a14"},
{file = "openai-0.27.10.tar.gz", hash = "sha256:60e09edf7100080283688748c6803b7b3b52d5a55d21890f3815292a0552d83b"},
]
[package.dependencies]
@ -4199,69 +4202,61 @@ files = [
[[package]]
name = "pandas"
version = "2.0.3"
version = "2.1.0"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
files = [
{file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"},
{file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"},
{file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"},
{file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"},
{file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"},
{file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"},
{file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"},
{file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"},
{file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"},
{file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"},
{file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"},
{file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"},
{file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"},
{file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"},
{file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"},
{file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"},
{file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"},
{file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"},
{file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"},
{file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"},
{file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"},
{file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"},
{file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"},
{file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"},
{file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"},
{file = "pandas-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40dd20439ff94f1b2ed55b393ecee9cb6f3b08104c2c40b0cb7186a2f0046242"},
{file = "pandas-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4f38e4fedeba580285eaac7ede4f686c6701a9e618d8a857b138a126d067f2f"},
{file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6a0fe052cf27ceb29be9429428b4918f3740e37ff185658f40d8702f0b3e09"},
{file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d81e1813191070440d4c7a413cb673052b3b4a984ffd86b8dd468c45742d3cc"},
{file = "pandas-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb20252720b1cc1b7d0b2879ffc7e0542dd568f24d7c4b2347cb035206936421"},
{file = "pandas-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:38f74ef7ebc0ffb43b3d633e23d74882bce7e27bfa09607f3c5d3e03ffd9a4a5"},
{file = "pandas-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cda72cc8c4761c8f1d97b169661f23a86b16fdb240bdc341173aee17e4d6cedd"},
{file = "pandas-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d97daeac0db8c993420b10da4f5f5b39b01fc9ca689a17844e07c0a35ac96b4b"},
{file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c58b1113892e0c8078f006a167cc210a92bdae23322bb4614f2f0b7a4b510f"},
{file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:629124923bcf798965b054a540f9ccdfd60f71361255c81fa1ecd94a904b9dd3"},
{file = "pandas-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:70cf866af3ab346a10debba8ea78077cf3a8cd14bd5e4bed3d41555a3280041c"},
{file = "pandas-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d53c8c1001f6a192ff1de1efe03b31a423d0eee2e9e855e69d004308e046e694"},
{file = "pandas-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f100b3876b8c6d1a2c66207288ead435dc71041ee4aea789e55ef0e06408cb"},
{file = "pandas-2.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28f330845ad21c11db51e02d8d69acc9035edfd1116926ff7245c7215db57957"},
{file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a6ccf0963db88f9b12df6720e55f337447aea217f426a22d71f4213a3099a6"},
{file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99e678180bc59b0c9443314297bddce4ad35727a1a2656dbe585fd78710b3b9"},
{file = "pandas-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b31da36d376d50a1a492efb18097b9101bdbd8b3fbb3f49006e02d4495d4c644"},
{file = "pandas-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0164b85937707ec7f70b34a6c3a578dbf0f50787f910f21ca3b26a7fd3363437"},
{file = "pandas-2.1.0.tar.gz", hash = "sha256:62c24c7fc59e42b775ce0679cfa7b14a5f9bfb7643cfbe708c960699e05fb918"},
]
[package.dependencies]
numpy = [
{version = ">=1.20.3", markers = "python_version < \"3.10\""},
{version = ">=1.21.0", markers = "python_version >= \"3.10\""},
]
numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""}
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
tzdata = ">=2022.1"
[package.extras]
all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"]
aws = ["s3fs (>=2021.08.0)"]
clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"]
compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"]
computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"]
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"]
all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"]
aws = ["s3fs (>=2022.05.0)"]
clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"]
compression = ["zstandard (>=0.17.0)"]
computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"]
consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"]
feather = ["pyarrow (>=7.0.0)"]
fss = ["fsspec (>=2021.07.0)"]
gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"]
hdf5 = ["tables (>=3.6.1)"]
html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"]
mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"]
output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"]
fss = ["fsspec (>=2022.05.0)"]
gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"]
hdf5 = ["tables (>=3.7.0)"]
html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"]
mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"]
output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"]
parquet = ["pyarrow (>=7.0.0)"]
performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"]
performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"]
plot = ["matplotlib (>=3.6.1)"]
postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"]
spss = ["pyreadstat (>=1.1.2)"]
sql-other = ["SQLAlchemy (>=1.4.16)"]
test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
xml = ["lxml (>=4.6.3)"]
postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"]
spss = ["pyreadstat (>=1.1.5)"]
sql-other = ["SQLAlchemy (>=1.4.36)"]
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
xml = ["lxml (>=4.8.0)"]
[[package]]
name = "pandas-stubs"
@ -4845,41 +4840,41 @@ files = [
[[package]]
name = "pulsar-client"
version = "3.2.0"
version = "3.3.0"
description = "Apache Pulsar Python client library"
optional = false
python-versions = "*"
files = [
{file = "pulsar_client-3.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:da53bbe1903026ca1253d36a67bde0ae88513497091658aee8c5514c3e567483"},
{file = "pulsar_client-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec595a71b7a25f1a72a1350efd6680a511b53253c3cac1911ba3d6c4d71fa64c"},
{file = "pulsar_client-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3557c65463d74ec8d2864752389beb06761ab591dd134a164e0b1303c66719b"},
{file = "pulsar_client-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d51dc76fec48217489bde95754ad58288c9389361de42f5a27d64e19840d27fb"},
{file = "pulsar_client-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9ef2baf85311e0fe1b98342fdafbb93a1818a08ef999eaa524234fedf6f3b941"},
{file = "pulsar_client-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:0928b02beda0c98e77178f4e30e962ddb8ee8c3320e4c7304a78b0796e976523"},
{file = "pulsar_client-3.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:584f44b03474a69906be711a597a4d516263a55be31e49fc07be503dc8406821"},
{file = "pulsar_client-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a637b9a3b30860c61e68a7b8ea650e0987d89e82f73b6a3df1ab662a6438fdda"},
{file = "pulsar_client-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4a187fdc5febcf16f725179dcf2c476f31eeebd8353794d91754a3202dd5072"},
{file = "pulsar_client-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5ff879f868cf1fd29db99f39fdb22b3ec3e749c648aca28526689756d922d1c5"},
{file = "pulsar_client-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a5f85d0cc414f739a5b51d843f213b54b2cd768c3a34f7c27cca410712b1f81"},
{file = "pulsar_client-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:4fe748283848d829a80c0323558faeebea4c240d69fa58314ac90344f6999d17"},
{file = "pulsar_client-3.2.0-cp37-cp37m-macosx_10_15_universal2.whl", hash = "sha256:06b91c26def86dbbc35be15257999fd8a2afbadf32983916ea3eef44f4d4cab4"},
{file = "pulsar_client-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39ec897bc8d232e6b118793378fc662a844334b829a28a1b4ad1c5fe8d019135"},
{file = "pulsar_client-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa37c96c25c1b5aff3bad0fd0194b385ec190b2c67a2f439ac91577f81ae18d3"},
{file = "pulsar_client-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d49cdd4d1b7fc2e80d100acf14e6fd3898f6e099e403fc56ed22a690245b2fec"},
{file = "pulsar_client-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0058ca3191fd24528ccf94dba6f12e4093831454a2597166f96900d0717271bf"},
{file = "pulsar_client-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:cb69b0411008e0b56df51de0aab20aa1c1a12aef3019b9ceba89afbae1f07fe2"},
{file = "pulsar_client-3.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:f7d33e99602352df7a30707eab4e5781654602212fb618928bffb5523f2bcf35"},
{file = "pulsar_client-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad1ac15a175ca90555c681a4d0134568771c6346b97a172f3ef14006556a50ae"},
{file = "pulsar_client-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369e08ef1d5cb196dd9271039928800f90b4701a9c9df90bc068b44260d2fb11"},
{file = "pulsar_client-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a52ba2b6736a2ebeed31b590e75d417dda149e333461655860efa84d898a3eb4"},
{file = "pulsar_client-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c801334b3b569b23976481a2922bcea0c6dd990fc26544658dd9e9c8f78ca36"},
{file = "pulsar_client-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cd01fd419280e9013d1655bc53662248be2656b623b1506480e1a985aa7dadd2"},
{file = "pulsar_client-3.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:0abe54d84db76435a6cd88ce27610352cabc7efae9fa3e7f874e032ec2ca0b3f"},
{file = "pulsar_client-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a1b6a806eb4819d8cbab1c4ae44ebf2110a94204a46c365f5757e1455252f2"},
{file = "pulsar_client-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ea2a6b75ae0e303d522e5b57c75a4ff03dc18b9bfc14151fb14dfaf5866f17"},
{file = "pulsar_client-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:be6d3a9b2e1db3b6d1a7db5e13f7b4ed420674cf072cdb520fb004c4cd54c0af"},
{file = "pulsar_client-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6b733e6239ffb505f7084df0175baf9d0215f14d0a02e9bbd1fdf71a2d6ea17"},
{file = "pulsar_client-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:edc2135d02b4793efb086edca0ffaa6e8ac9133961c2cdc17ae487e0a53da481"},
{file = "pulsar_client-3.3.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:c31afd3e67a044ff93177df89e08febf214cc965e95ede097d9fe8755af00e01"},
{file = "pulsar_client-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f66982284571674b215324cc26b5c2f7c56c7043113c47a7084cb70d67a8afb"},
{file = "pulsar_client-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fe50a06f81c48a75a9b95c27a6446260039adca71d9face273740de96b2efca"},
{file = "pulsar_client-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d4c46a4b96a6e9919cfe220156d69a2ede8053d9ea1add4ada108abcf2ba9775"},
{file = "pulsar_client-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1e4b5d44b992c9b036286b483f3588c10b89c6047fb59d80c7474445997f4e10"},
{file = "pulsar_client-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:497a59ac6b650835a3b2c502f53477e5c98e5226998ca3f17c0b0a3eb4d67d08"},
{file = "pulsar_client-3.3.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:386e78ff52058d881780bae1f6e84ac9434ae0b01a8581755ca8cc0dc844a332"},
{file = "pulsar_client-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e4ecb780df58bcfd3918590bd3ff31ed79bccfbef3a1a60370642eb1e14a9d2"},
{file = "pulsar_client-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce1e215c252f22a6f26ca5e9076826041a04d88dc213b92c86b524be2774a64"},
{file = "pulsar_client-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b0fd5be73a4103986b9dbe3a66468cf8829371e34af87ff8f216e3980f4cbe"},
{file = "pulsar_client-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33656450536d83eed1563ff09692c2c415fb199d88e9ed97d701ca446a119e1b"},
{file = "pulsar_client-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce33de700b06583df8777e139d68cb4b4b3d0a2eac168d74278d8935f357fb10"},
{file = "pulsar_client-3.3.0-cp37-cp37m-macosx_10_15_universal2.whl", hash = "sha256:7b5dd25cf778d6c980d36c53081e843ea272afe7af4f0ad6394ae9513f94641b"},
{file = "pulsar_client-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33c4e6865fda62a2e460f823dce4d49ac2973a4459b8ff99eda5fdd6aaaebf46"},
{file = "pulsar_client-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1810ddc623c8de2675d17405ce47057a9a2b92298e708ce4d9564847f5ad904"},
{file = "pulsar_client-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8259c3b856eb6deaa1f93dce893ab18d99d36d102da5612c8e97a4fb41b70ab1"},
{file = "pulsar_client-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5e7a48b2e505cde758fd51a601b5da0671fa98c9baee38362aaaa3ab2b930c28"},
{file = "pulsar_client-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ede264385d47257b2f2b08ecde9181ec5338bea5639cc543d1856f01736778d2"},
{file = "pulsar_client-3.3.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:0f64c62746ccd5b65a0c505f5f40b9af1f147eb1fa2d8f9c90cd5c8b92dd8597"},
{file = "pulsar_client-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b84a20c9012e3c4ef1b7085acd7467197118c090b378dec27d773fb79d91556"},
{file = "pulsar_client-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4e15fa696e275ccb66d0791fdc19c4dea0420d81349c8055e485b134125e14f"},
{file = "pulsar_client-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:72cbb1bdcba2dd1265296b5ba65331622ee89c16db75edaad46dd7b90c6dd447"},
{file = "pulsar_client-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d54dd12955bf587dd46d9184444af5e853d9da2a14bbfb739ed2c7c3b78ce280"},
{file = "pulsar_client-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:43f98afdf0334b2b957a4d96f97a1fe8a7f7fd1e2631d40c3f00b4162f396485"},
{file = "pulsar_client-3.3.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:efe7c1e6a96daccc522c3567b6847ffa54c13e0f510d9a427b4aeff9fbebe54b"},
{file = "pulsar_client-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f28e94420090fceeb38e23fc744f3edf8710e48314ef5927d2b674a1d1e43ee0"},
{file = "pulsar_client-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c8f3eaa98e2351805ecb6efb6d5fedf47a314a3ce6af0e05ea1449ea7244ed"},
{file = "pulsar_client-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5e69750f8ae57e55fddf97b459ce0d8b38b2bb85f464a71e871ee6a86d893be7"},
{file = "pulsar_client-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7e147e5ba460c1818bc05254279a885b4e552bcafb8961d40e31f98d5ff46628"},
{file = "pulsar_client-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:694530af1d6c75fb81456fb509778c1868adee31e997ddece6e21678200182ea"},
]
[package.dependencies]
@ -6855,13 +6850,13 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
[[package]]
name = "transformers"
version = "4.32.0"
version = "4.32.1"
description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow"
optional = true
python-versions = ">=3.8.0"
files = [
{file = "transformers-4.32.0-py3-none-any.whl", hash = "sha256:32d8adf0ed76285508e7fd66657b4448ec1f882599ae6bf6f9c36bd7bf798402"},
{file = "transformers-4.32.0.tar.gz", hash = "sha256:ca510f9688d2fe7347abbbfbd13f2f6dcd3c8349870c8d0ed98beed5f579b354"},
{file = "transformers-4.32.1-py3-none-any.whl", hash = "sha256:b930d3dbd907a3f300cf49e54d63a56f8a0ab16b01a2c2a61ecff37c6de1da08"},
{file = "transformers-4.32.1.tar.gz", hash = "sha256:1edc8ae1de357d97c3d36b04412aa63d55e6fc0c4b39b419a7d380ed947d2252"},
]
[package.dependencies]
@ -7046,6 +7041,17 @@ files = [
{file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"},
]
[[package]]
name = "types-pywin32"
version = "306.0.0.4"
description = "Typing stubs for pywin32"
optional = false
python-versions = "*"
files = [
{file = "types-pywin32-306.0.0.4.tar.gz", hash = "sha256:ae4bbec80d535053236d4bebedf55f58dee89cf5883d277f0fa89e857f3ff337"},
{file = "types_pywin32-306.0.0.4-py3-none-any.whl", hash = "sha256:f76a343ed6933008af85e158063963f923e54f2f461e697b2929b4178c7b77a1"},
]
[[package]]
name = "types-pyyaml"
version = "6.0.12.11"
@ -7334,13 +7340,13 @@ files = [
[[package]]
name = "weaviate-client"
version = "3.23.1"
version = "3.23.2"
description = "A python native Weaviate client"
optional = false
python-versions = ">=3.8"
files = [
{file = "weaviate-client-3.23.1.tar.gz", hash = "sha256:035f395fb8b17008224dc8a9ca4459b7ef4a2b0449209ac0c8d0f2e3b9a77f59"},
{file = "weaviate_client-3.23.1-py3-none-any.whl", hash = "sha256:826b28237f7143ee4c51b988c5c37494760b4377a8536acc772a2194eb33f30c"},
{file = "weaviate-client-3.23.2.tar.gz", hash = "sha256:1c8c94df032dd2fa5a4ea615fc69ccb983ffad5cc02974f78c793839e61ac150"},
{file = "weaviate_client-3.23.2-py3-none-any.whl", hash = "sha256:88ffc38cca07806d64726cc74bc194c7da50b222aa4e2cd129f4c1f5e53e9b61"},
]
[package.dependencies]
@ -7778,4 +7784,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.11"
content-hash = "505fe04c51514ef25dd955b377a00185c4a2581770af3cc84db4c47477760048"
content-hash = "6523f2e35458c6d0b8d281e20dd2233180128805ea07199c073224b0d6f75ee7"

View file

@ -83,6 +83,8 @@ bcrypt = "^4.0.1"
python-jose = "^3.3.0"
metaphor-python = "^0.1.11"
markupsafe = "^2.1.3"
pywin32 = { version = "^306", markers = "sys_platform == 'win32'" }
loguru = "^0.7.1"
[tool.poetry.group.dev.dependencies]
black = "^23.1.0"
@ -102,6 +104,7 @@ types-python-jose = "^3.3.4.8"
types-passlib = "^1.7.7.13"
pytest-mock = "^3.11.1"
pytest-xdist = "^3.3.1"
types-pywin32 = "^306.0.0.4"
[tool.poetry.extras]

View file

@ -356,7 +356,7 @@ def superuser(
with session_getter(db_manager) as session:
from langflow.services.auth.utils import create_super_user
if create_super_user(session, username, password):
if create_super_user(db=session, username=username, password=password):
# Verify that the superuser was created
from langflow.services.database.models.user.user import User

View file

@ -30,10 +30,10 @@ def upgrade() -> None:
# and other related indices
if "flowstyle" in existing_tables:
op.drop_table("flowstyle")
if "ix_flowstyle_flow_id" in [
index["name"] for index in inspector.get_indexes("flowstyle")
]:
op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle")
if "ix_flowstyle_flow_id" in [
index["name"] for index in inspector.get_indexes("flowstyle")
]:
op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle")
existing_indices_flow = []
existing_fks_flow = []

View file

@ -1,3 +1,4 @@
from typing import Optional
from langflow.template.frontend_node.base import FrontendNode
from pydantic import BaseModel, validator
@ -20,7 +21,8 @@ class FrontendNodeRequest(FrontendNode):
class ValidatePromptRequest(BaseModel):
name: str
template: str
frontend_node: FrontendNodeRequest
# optional for tweak call
frontend_node: Optional[FrontendNodeRequest] = None
# Build ValidationResponse class for {"imports": {"errors": []}, "function": {"errors": []}}
@ -39,7 +41,8 @@ class CodeValidationResponse(BaseModel):
class PromptValidationResponse(BaseModel):
input_variables: list
frontend_node: FrontendNodeRequest
# object return for tweak call
frontend_node: Optional[FrontendNodeRequest] = None
INVALID_CHARACTERS = {

View file

@ -10,7 +10,7 @@ from fastapi import WebSocket
from langchain.schema import AgentAction, LLMResult, AgentFinish
from langflow.utils.logger import logger
from loguru import logger
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py

View file

@ -11,13 +11,15 @@ from fastapi.responses import StreamingResponse
from langflow.api.utils import build_input_keys_response
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
from langflow.services import service_manager, ServiceType
from langflow.graph.graph.base import Graph
from langflow.services.auth.utils import get_current_active_user, get_current_user
from langflow.services.utils import get_session
from langflow.utils.logger import logger
from loguru import logger
from langflow.services.utils import get_chat_manager, get_session
from cachetools import LRUCache
from sqlmodel import Session
from langflow.services.chat.manager import ChatManager
router = APIRouter(tags=["Chat"])
@ -30,29 +32,48 @@ async def chat(
websocket: WebSocket,
token: str = Query(...),
db: Session = Depends(get_session),
chat_manager: "ChatManager" = Depends(get_chat_manager),
):
"""Websocket endpoint for chat."""
try:
await websocket.accept()
user = await get_current_user(token, db)
if not user:
await websocket.close(
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
)
if not user.is_active:
raise HTTPException(status_code=401, detail="Invalid token")
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
await websocket.close(
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
)
if client_id in chat_manager.in_memory_cache:
await chat_manager.handle_websocket(client_id, websocket)
else:
# We accept the connection but close it immediately
# if the flow is not built yet
await websocket.accept()
message = "Please, build the flow before sending messages"
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=message)
except WebSocketException as exc:
logger.error(f"Websocket error: {exc}")
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
except Exception as exc:
logger.error(f"Error in chat websocket: {exc}")
messsage = exc.detail if isinstance(exc, HTTPException) else str(exc)
if "Could not validate credentials" in str(exc):
await websocket.close(
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
)
else:
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=messsage)
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
async def init_build(
graph_data: dict, flow_id: str, current_user=Depends(get_current_active_user)
graph_data: dict,
flow_id: str,
current_user=Depends(get_current_active_user),
chat_manager: "ChatManager" = Depends(get_chat_manager),
):
"""Initialize the build by storing graph data and returning a unique session ID."""
@ -67,7 +88,6 @@ async def init_build(
return InitResponse(flowId=flow_id)
# Delete from cache if already exists
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
if flow_id in chat_manager.in_memory_cache:
with chat_manager.in_memory_cache._lock:
chat_manager.in_memory_cache.delete(flow_id)
@ -103,7 +123,9 @@ async def build_status(flow_id: str):
@router.get("/build/stream/{flow_id}", response_class=StreamingResponse)
async def stream_build(flow_id: str):
async def stream_build(
flow_id: str, chat_manager: "ChatManager" = Depends(get_chat_manager)
):
"""Stream the build process based on stored flow data."""
async def event_stream(flow_id):
@ -182,7 +204,6 @@ async def stream_build(flow_id: str):
"handle_keys": [],
}
yield str(StreamData(event="message", data=input_keys_response))
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
chat_manager.set_cache(flow_id, langchain_object)
# We need to reset the chat history
chat_manager.chat_history.empty_history(flow_id)

View file

@ -7,7 +7,7 @@ from langflow.services.database.models.flow import Flow
from langflow.processing.process import process_graph_cached, process_tweaks
from langflow.services.database.models.user.user import User
from langflow.services.utils import get_settings_manager
from langflow.utils.logger import logger
from loguru import logger
from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status
import sqlalchemy as sa
from langflow.interface.custom.custom_component import CustomComponent
@ -34,14 +34,15 @@ from sqlmodel import Session
router = APIRouter(tags=["Base"])
@router.get("/all")
def get_all(current_user: User = Depends(get_current_active_user)):
@router.get("/all", dependencies=[Depends(get_current_active_user)])
def get_all(
settings_manager=Depends(get_settings_manager),
):
logger.debug("Building langchain types dict")
native_components = build_langchain_types_dict()
# custom_components is a list of dicts
# need to merge all the keys into one dict
custom_components_from_file: dict[str, Any] = {}
settings_manager = get_settings_manager()
if settings_manager.settings.COMPONENTS_PATH:
logger.info(
f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}"

View file

@ -83,6 +83,7 @@ def update_flow(
flow_id: UUID,
flow: FlowUpdate,
current_user: User = Depends(get_current_active_user),
settings_manager=Depends(get_settings_manager),
):
"""Update a flow."""
@ -90,7 +91,6 @@ def update_flow(
if not db_flow:
raise HTTPException(status_code=404, detail="Flow not found")
flow_data = flow.dict(exclude_unset=True)
settings_manager = get_settings_manager()
if settings_manager.settings.REMOVE_API_KEYS:
flow_data = remove_api_keys(flow_data)
for key, value in flow_data.items():

View file

@ -34,9 +34,9 @@ async def login_to_get_access_token(
@router.get("/auto_login")
async def auto_login(db: Session = Depends(get_session)):
settings_manager = get_settings_manager()
async def auto_login(
db: Session = Depends(get_session), settings_manager=Depends(get_settings_manager)
):
if settings_manager.auth_settings.AUTO_LOGIN:
return create_user_longterm_token(db)

View file

@ -29,7 +29,7 @@ router = APIRouter(tags=["Users"])
@router.post("/user", response_model=UserRead, status_code=201)
def add_user(
user: UserCreate,
db: Session = Depends(get_session),
session: Session = Depends(get_session),
) -> User:
"""
Add a new user to the database.
@ -38,11 +38,11 @@ def add_user(
try:
new_user.password = get_password_hash(user.password)
db.add(new_user)
db.commit()
db.refresh(new_user)
session.add(new_user)
session.commit()
session.refresh(new_user)
except IntegrityError as e:
db.rollback()
session.rollback()
raise HTTPException(
status_code=400, detail="This username is unavailable."
) from e
@ -65,16 +65,16 @@ def read_all_users(
skip: int = 0,
limit: int = 10,
current_user: Session = Depends(get_current_active_superuser),
db: Session = Depends(get_session),
session: Session = Depends(get_session),
) -> UsersResponse:
"""
Retrieve a list of users from the database with pagination.
"""
query = select(User).offset(skip).limit(limit)
users = db.execute(query).fetchall()
users = session.execute(query).fetchall()
count_query = select(func.count()).select_from(User) # type: ignore
total_count = db.execute(count_query).scalar()
total_count = session.execute(count_query).scalar()
return UsersResponse(
total_count=total_count, # type: ignore
@ -87,19 +87,19 @@ def patch_user(
user_id: UUID,
user: UserUpdate,
_: Session = Depends(get_current_active_user),
db: Session = Depends(get_session),
session: Session = Depends(get_session),
) -> User:
"""
Update an existing user's data.
"""
return update_user(user_id, user, db)
return update_user(user_id, user, session)
@router.delete("/user/{user_id}")
def delete_user(
user_id: UUID,
current_user: User = Depends(get_current_active_superuser),
db: Session = Depends(get_session),
session: Session = Depends(get_session),
) -> dict:
"""
Delete a user from the database.
@ -113,12 +113,12 @@ def delete_user(
status_code=403, detail="You don't have the permission to delete this user"
)
user_db = db.query(User).filter(User.id == user_id).first()
user_db = session.query(User).filter(User.id == user_id).first()
if not user_db:
raise HTTPException(status_code=404, detail="User not found")
db.delete(user_db)
db.commit()
session.delete(user_db)
session.commit()
return {"detail": "User deleted"}
@ -126,7 +126,7 @@ def delete_user(
# TODO: REMOVE - Just for testing purposes
@router.post("/super_user", response_model=User)
def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
db: Session = Depends(get_session),
session: Session = Depends(get_session),
) -> User:
"""
Add a superuser for testing purposes.
@ -141,11 +141,11 @@ def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
)
try:
db.add(new_user)
db.commit()
db.refresh(new_user)
session.add(new_user)
session.commit()
session.refresh(new_user)
except IntegrityError as e:
db.rollback()
session.rollback()
raise HTTPException(status_code=400, detail="User exists") from e
return new_user

View file

@ -8,7 +8,7 @@ from langflow.api.v1.base import (
validate_prompt,
)
from langflow.template.field.base import TemplateField
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.validate import validate_code
# build router
@ -31,7 +31,12 @@ def post_validate_code(code: Code):
def post_validate_prompt(prompt_request: ValidatePromptRequest):
try:
input_variables = validate_prompt(prompt_request.template)
# Check if frontend_node is None before proceeding to avoid attempting to update a non-existent node.
if prompt_request.frontend_node is None:
return PromptValidationResponse(
input_variables=input_variables,
frontend_node=None,
)
old_custom_fields = get_old_custom_fields(prompt_request)
add_new_variables_to_template(input_variables, prompt_request)

View file

@ -1,4 +1,4 @@
from langflow.utils.logger import logger
from loguru import logger
from typing import TYPE_CHECKING
from pydantic import BaseModel, Field
from typing import List, Optional

View file

@ -10,7 +10,7 @@ from langflow.graph.vertex.types import (
)
from langflow.interface.tools.constants import FILE_TOOLS
from langflow.utils import payload
from langflow.utils.logger import logger
from loguru import logger
from langchain.chains.base import Chain

View file

@ -3,7 +3,7 @@ from langflow.graph.utils import UnbuiltObject
from langflow.interface.initialize import loading
from langflow.interface.listing import lazy_load_dict
from langflow.utils.constants import DIRECT_TYPES
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import sync_to_async

View file

@ -8,7 +8,7 @@ from langflow.interface.base import LangChainTypeCreator
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.agents import AgentFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -8,7 +8,7 @@ from pydantic import BaseModel
from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode
from langflow.template.template.base import Template
from langflow.utils.logger import logger
from loguru import logger
# Assuming necessary imports for Field, Template, and FrontendNode classes

View file

@ -6,7 +6,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.chains import ChainFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method
from langchain import chains
from langchain_experimental.sql import SQLDatabaseChain # type: ignore

View file

@ -8,7 +8,7 @@ from langflow.interface.custom.custom_component import CustomComponent
from langflow.template.frontend_node.custom_components import (
CustomComponentFrontendNode,
)
from langflow.utils.logger import logger
from loguru import logger
# Assuming necessary imports for Field, Template, and FrontendNode classes

View file

@ -1,7 +1,7 @@
import os
import ast
import zlib
from langflow.utils.logger import logger
from loguru import logger
class CustomComponentPathValueError(ValueError):

View file

@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -34,7 +34,7 @@ from langflow.utils import validate
from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore
from langchain.document_loaders.base import BaseLoader
from langflow.utils.logger import logger
from loguru import logger
if TYPE_CHECKING:
from langflow import CustomComponent

View file

@ -5,7 +5,7 @@ from langflow.interface.custom_lists import llm_type_to_cls_dict
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.llms import LLMFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.memories import MemoryFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method
from langflow.custom.customs import get_custom_nodes

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.prompts import PromptFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.retrievers import RetrieverFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_method, build_template_from_class

View file

@ -1,7 +1,7 @@
from typing import Any, Dict, Tuple
from langflow.services.cache.utils import memoize_dict
from langflow.graph import Graph
from langflow.utils.logger import logger
from loguru import logger
@memoize_dict(maxsize=10)

View file

@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.interface.base import LangChainTypeCreator
from langflow.interface.importing.utils import import_class, import_module
from langflow.services.utils import get_settings_manager
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -3,7 +3,7 @@ import inspect
from typing import Dict, Union
from langchain.agents.tools import Tool
from langflow.utils.logger import logger
from loguru import logger
def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:

View file

@ -29,7 +29,7 @@ from langflow.template.frontend_node.custom_components import (
from langflow.interface.retrievers.base import retriever_creator
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import get_base_classes
import re

View file

@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class

View file

@ -8,7 +8,7 @@ import re
import yaml
from langchain.base_language import BaseLanguageModel
from PIL.Image import Image
from langflow.utils.logger import logger
from loguru import logger
from langflow.services.chat.config import ChatConfig
from langflow.services.utils import get_settings_manager

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_method

View file

@ -3,7 +3,7 @@ from typing import Dict, List, Optional
from langchain import requests, sql_database
from langflow.interface.base import LangChainTypeCreator
from langflow.utils.logger import logger
from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -10,7 +10,7 @@ from langflow.api import router
from langflow.interface.utils import setup_llm_caching
from langflow.services.database.utils import initialize_database
from langflow.services.manager import initialize_services
from langflow.services.manager import initialize_services, teardown_services
from langflow.utils.logger import configure
@ -40,6 +40,7 @@ def create_app():
app.on_event("startup")(initialize_services)
app.on_event("startup")(initialize_database)
app.on_event("startup")(setup_llm_caching)
app.on_event("shutdown")(teardown_services)
return app

View file

@ -4,7 +4,7 @@ from langflow.api.v1.callback import (
StreamingLLMCallbackHandler,
)
from langflow.processing.process import fix_memory_inputs, format_actions
from langflow.utils.logger import logger
from loguru import logger
from langchain.agents.agent import AgentExecutor

View file

@ -6,7 +6,7 @@ from langflow.interface.run import (
get_memory_key,
update_memory_keys,
)
from langflow.utils.logger import logger
from loguru import logger
from langflow.graph import Graph
from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore

View file

@ -37,7 +37,12 @@ async def api_key_security(
result: Optional[Union[ApiKey, User]] = None
if settings_manager.auth_settings.AUTO_LOGIN:
# Get the first user
settings_manager.auth_settings.FIRST_SUPERUSER
if not settings_manager.auth_settings.FIRST_SUPERUSER:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing first superuser credentials",
)
result = get_user_by_username(
db, settings_manager.auth_settings.FIRST_SUPERUSER
)
@ -80,6 +85,9 @@ async def get_current_user(
if isinstance(token, Coroutine):
token = await token
if settings_manager.auth_settings.SECRET_KEY is None:
raise credentials_exception
try:
payload = jwt.decode(
token,
@ -88,6 +96,11 @@ async def get_current_user(
)
user_id: UUID = payload.get("sub") # type: ignore
token_type: str = payload.get("type") # type: ignore
if expires := payload.get("exp", None):
expires_datetime = datetime.fromtimestamp(expires, timezone.utc)
# TypeError: can't compare offset-naive and offset-aware datetimes
if datetime.now(timezone.utc) > expires_datetime:
raise credentials_exception
if user_id is None or token_type:
raise credentials_exception
@ -145,22 +158,16 @@ def create_token(data: dict, expires_delta: timedelta):
def create_super_user(
username: str,
password: str,
db: Session = Depends(get_session),
username: Optional[str] = None,
password: Optional[str] = None,
) -> User:
settings_manager = get_settings_manager()
super_user = get_user_by_username(
db, username or settings_manager.auth_settings.FIRST_SUPERUSER
)
super_user = get_user_by_username(db, username)
if not super_user:
super_user = User(
username=username or settings_manager.auth_settings.FIRST_SUPERUSER,
password=get_password_hash(
password or settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
),
username=username,
password=get_password_hash(password),
is_superuser=True,
is_active=True,
last_login_at=None,
@ -174,7 +181,15 @@ def create_super_user(
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
super_user = create_super_user(db)
settings_manager = get_settings_manager()
username = settings_manager.auth_settings.FIRST_SUPERUSER
password = settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
if not username or not password:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing first superuser credentials",
)
super_user = create_super_user(db=db, username=username, password=password)
access_token_expires_longterm = timedelta(days=365)
access_token = create_token(

View file

@ -1,2 +1,8 @@
class Service:
from abc import ABC
class Service(ABC):
name: str
def teardown(self):
pass

View file

@ -7,7 +7,7 @@ from langflow.services.cache.manager import Subject
from langflow.services.chat.utils import process_graph
from langflow.interface.utils import pil_to_base64
from langflow.services.schema import ServiceType
from langflow.utils.logger import logger
from loguru import logger
import asyncio
@ -92,7 +92,6 @@ class ChatManager(Service):
)
async def connect(self, client_id: str, websocket: WebSocket):
await websocket.accept()
self.active_connections[client_id] = websocket
def disconnect(self, client_id: str):

View file

@ -2,7 +2,7 @@ from fastapi import WebSocket
from langflow.api.v1.schemas import ChatMessage
from langflow.processing.base import get_result_and_steps
from langflow.interface.utils import try_setting_streaming_options
from langflow.utils.logger import logger
from loguru import logger
async def process_graph(

View file

@ -1,12 +1,13 @@
from pathlib import Path
from typing import TYPE_CHECKING
from langflow.services.base import Service
from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults
from langflow.services.utils import get_settings_manager
from sqlalchemy import inspect
import sqlalchemy as sa
from sqlmodel import SQLModel, Session, create_engine
from langflow.utils.logger import logger
from loguru import logger
from alembic.config import Config
from alembic import command
from langflow.services.database import models # noqa
@ -88,7 +89,7 @@ class DatabaseManager(Service):
for table in legacy_tables:
if table in inspector.get_table_names():
logger.warn(f"Legacy table exists: {table}")
logger.warning(f"Legacy table exists: {table}")
return True
@ -159,3 +160,23 @@ class DatabaseManager(Service):
)
logger.debug("Database and tables created successfully")
def teardown(self):
logger.debug("Tearing down database")
try:
settings_manager = get_settings_manager()
# remove the default superuser if auto_login is enabled
# using the FIRST_SUPERUSER to get the user
if settings_manager.auth_settings.AUTO_LOGIN:
logger.debug("Removing default superuser")
username = settings_manager.auth_settings.FIRST_SUPERUSER
with Session(self.engine) as session:
user = get_user_by_username(session, username)
session.delete(user)
session.commit()
logger.debug("Default superuser removed")
except Exception as exc:
logger.error(f"Error tearing down database: {exc}")
self.engine.dispose()

View file

@ -1,6 +1,6 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING
from langflow.utils.logger import logger
from loguru import logger
from contextlib import contextmanager
from alembic.util.exc import CommandError
from sqlmodel import Session

View file

@ -1,5 +1,6 @@
from langflow.services.schema import ServiceType
from typing import TYPE_CHECKING, List, Optional
from loguru import logger
if TYPE_CHECKING:
from langflow.services.factory import ServiceFactory
@ -42,6 +43,7 @@ class ServiceManager:
"""
Create a new service given its name, handling dependencies.
"""
logger.debug(f"Create service {service_name}")
self._validate_service_creation(service_name)
# Create dependencies first
@ -74,9 +76,21 @@ class ServiceManager:
Update a service by its name.
"""
if service_name in self.services:
logger.debug(f"Update service {service_name}")
self.services.pop(service_name, None)
self.get(service_name)
def teardown(self):
"""
Teardown all the services.
"""
for service in self.services.values():
logger.debug(f"Teardown service {service.name}")
service.teardown()
self.services = {}
self.factories = {}
self.dependencies = {}
service_manager = ServiceManager()
@ -121,7 +135,7 @@ def initialize_session_manager():
"""
Initialize the session manager.
"""
from langflow.services.session import factory as session_manager_factory
from langflow.services.session import factory as session_manager_factory # type: ignore
from langflow.services.cache import factory as cache_factory
initialize_settings_manager()
@ -134,3 +148,10 @@ def initialize_session_manager():
session_manager_factory.SessionManagerFactory(),
dependencies=[ServiceType.CACHE_MANAGER],
)
def teardown_services():
"""
Teardown all the services.
"""
service_manager.teardown()

View file

@ -1,13 +1,22 @@
from pathlib import Path
from typing import Optional
import secrets
from langflow.services.settings.utils import read_secret_from_file, write_secret_to_file
from pydantic import BaseSettings
from pydantic import BaseSettings, Field, validator
from passlib.context import CryptContext
from loguru import logger
class AuthSettings(BaseSettings):
# Login settings
SECRET_KEY: str = secrets.token_hex(32)
CONFIG_DIR: str
SECRET_KEY: str = Field(
default="",
description="Secret key for JWT. If not provided, a random one will be generated.",
env="LANGFLOW_SECRET_KEY",
allow_mutation=False,
)
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
REFRESH_TOKEN_EXPIRE_MINUTES: int = 70
@ -31,3 +40,33 @@ class AuthSettings(BaseSettings):
validate_assignment = True
extra = "ignore"
env_prefix = "LANGFLOW_"
@validator("SECRET_KEY", pre=True)
def get_secret_key(cls, value, values):
config_dir = values.get("CONFIG_DIR")
if not config_dir:
logger.debug("No CONFIG_DIR provided, not saving secret key")
return value or secrets.token_urlsafe(32)
secret_key_path = Path(config_dir) / "secret_key"
if value:
logger.debug("Secret key provided")
write_secret_to_file(secret_key_path, value)
else:
logger.debug("No secret key provided, generating a random one")
if secret_key_path.exists():
value = read_secret_from_file(secret_key_path)
logger.debug("Loaded secret key")
if not value:
value = secrets.token_urlsafe(32)
write_secret_to_file(secret_key_path, value)
logger.debug("Saved secret key")
else:
value = secrets.token_urlsafe(32)
write_secret_to_file(secret_key_path, value)
logger.debug("Saved secret key")
return value

View file

@ -8,7 +8,7 @@ from pathlib import Path
import yaml
from pydantic import BaseSettings, root_validator, validator
from langflow.utils.logger import logger
from loguru import logger
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")

View file

@ -1,7 +1,7 @@
from langflow.services.base import Service
from langflow.services.settings.auth import AuthSettings
from langflow.services.settings.base import Settings
from langflow.utils.logger import logger
from loguru import logger
import os
import yaml
@ -35,5 +35,10 @@ class SettingsManager(Service):
)
settings = Settings(**settings_dict)
auth_settings = AuthSettings()
if not settings.CONFIG_DIR:
raise ValueError("CONFIG_DIR must be set in settings")
auth_settings = AuthSettings(
CONFIG_DIR=settings.CONFIG_DIR,
)
return cls(settings, auth_settings)

View file

@ -0,0 +1,47 @@
import os
from pathlib import Path
import platform
from loguru import logger
def set_secure_permissions(file_path):
if platform.system() in ["Linux", "Darwin"]: # Unix/Linux/Mac
os.chmod(file_path, 0o600)
elif platform.system() == "Windows":
import win32api
import win32con
import win32security
user, domain, _ = win32security.LookupAccountName("", win32api.GetUserName())
sd = win32security.GetFileSecurity(
file_path, win32security.DACL_SECURITY_INFORMATION
)
dacl = win32security.ACL()
# Set the new DACL for the file: read and write access for the owner, no access for everyone else
dacl.AddAccessAllowedAce(
win32security.ACL_REVISION,
win32con.GENERIC_READ | win32con.GENERIC_WRITE,
user,
)
sd.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(
file_path, win32security.DACL_SECURITY_INFORMATION, sd
)
else:
print("Unsupported OS")
def write_secret_to_file(path: Path, value: str) -> None:
with path.open("wb") as f:
f.write(value.encode("utf-8"))
try:
set_secure_permissions(path)
except Exception:
logger.error("Failed to set secure permissions on secret key")
def read_secret_from_file(path: Path) -> str:
with path.open("r") as f:
return f.read()

View file

@ -1,10 +1,12 @@
from langflow.services import ServiceType, service_manager
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Generator
if TYPE_CHECKING:
from langflow.services.database.manager import DatabaseManager
from langflow.services.settings.manager import SettingsManager
from langflow.services.chat.manager import ChatManager
from sqlmodel import Session
def get_settings_manager() -> "SettingsManager":
@ -15,6 +17,10 @@ def get_db_manager() -> "DatabaseManager":
return service_manager.get(ServiceType.DATABASE_MANAGER)
def get_session():
def get_session() -> Generator["Session", None, None]:
db_manager = service_manager.get(ServiceType.DATABASE_MANAGER)
yield from db_manager.get_session()
def get_chat_manager() -> "ChatManager":
return service_manager.get(ServiceType.CHAT_MANAGER)

View file

@ -1,30 +1,35 @@
import logging
from typing import Optional
from loguru import logger
from pathlib import Path
from rich.logging import RichHandler
logger = logging.getLogger("langflow")
def configure(log_level: str = "DEBUG", log_file: Optional[Path] = None):
log_format = "<green>{time:HH:mm:ss}</green> - <level>{level: <8}</level> - <level>{message}</level>"
logger.remove() # Remove default handlers
def configure(log_level: str = "DEBUG", log_file: Path = None): # type: ignore
log_format = "%(asctime)s - %(levelname)s - %(message)s"
log_level_value = getattr(logging, log_level.upper(), logging.INFO)
logging.basicConfig(
level=log_level_value,
format=log_format,
datefmt="[%X]",
handlers=[RichHandler(rich_tracebacks=True)],
# Configure loguru to use RichHandler
logger.configure(
handlers=[
{
"sink": RichHandler(rich_tracebacks=True, markup=True),
"format": log_format,
"level": log_level.upper(),
}
]
)
if log_file:
log_file = Path(log_file)
log_file.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(logging.Formatter(log_format))
logger.addHandler(file_handler)
logger.add(
sink=str(log_file),
level=log_level.upper(),
format=log_format,
rotation="10 MB", # Log rotation based on file size
)
logger.info(f"Logger set up with log level: {log_level_value}({log_level})")
logger.info(f"Logger set up with log level: {log_level}")
if log_file:
logger.info(f"Log file: {log_file}")

View file

@ -8,11 +8,11 @@
"name": "langflow",
"version": "0.1.2",
"dependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@headlessui/react": "^1.7.10",
"@heroicons/react": "^2.0.15",
"@mui/material": "^5.11.9",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"@mui/material": "^5.14.7",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
@ -29,20 +29,20 @@
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.6",
"@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3",
"@tabler/icons-react": "^2.32.0",
"@tailwindcss/forms": "^0.5.6",
"@tailwindcss/line-clamp": "^0.4.4",
"@types/axios": "^0.14.0",
"accordion": "^3.0.2",
"ace-builds": "^1.16.0",
"ace-builds": "^1.24.1",
"add": "^2.0.6",
"ansi-to-html": "^0.7.2",
"axios": "^1.3.2",
"axios": "^1.5.0",
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0",
"class-variance-authority": "^0.6.1",
"clsx": "^1.2.1",
"dompurify": "^3.0.4",
"esbuild": "^0.17.18",
"dompurify": "^3.0.5",
"esbuild": "^0.17.19",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",
"moment": "^2.29.4",
@ -50,51 +50,51 @@
"react-ace": "^10.1.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.2",
"react-icons": "^4.8.0",
"react-error-boundary": "^4.0.11",
"react-icons": "^4.10.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.8.1",
"react-router-dom": "^6.15.0",
"react-syntax-highlighter": "^15.5.0",
"react-tabs": "^6.0.0",
"react-tooltip": "^5.13.1",
"react-tabs": "^6.0.2",
"react-tooltip": "^5.21.1",
"reactflow": "^11.8.3",
"rehype-mathjax": "^4.0.2",
"rehype-mathjax": "^4.0.3",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"shadcn-ui": "^0.2.2",
"shadcn-ui": "^0.2.3",
"short-unique-id": "^4.4.4",
"switch": "^0.0.0",
"table": "^6.8.1",
"tailwind-merge": "^1.13.0",
"tailwindcss-animate": "^1.0.5",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^9.0.0",
"vite-plugin-svgr": "^3.2.0",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.62",
"@swc/core": "^1.3.80",
"@tailwindcss/typography": "^0.5.9",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/lodash": "^4.14.194",
"@types/node": "^16.18.12",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/uuid": "^9.0.1",
"@vitejs/plugin-react-swc": "^3.0.0",
"autoprefixer": "^10.4.14",
"daisyui": "^3.1.1",
"postcss": "^8.4.23",
"@types/lodash": "^4.14.197",
"@types/node": "^16.18.46",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/uuid": "^9.0.2",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.15",
"daisyui": "^3.6.3",
"postcss": "^8.4.29",
"prettier": "^2.8.8",
"prettier-plugin-organize-imports": "^3.2.2",
"prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "^3.3.2",
"typescript": "^5.0.2",
"vite": "^4.3.9"
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2",
"vite": "^4.4.9"
}
},
"node_modules/@adobe/css-tools": {
@ -5522,9 +5522,9 @@
}
},
"node_modules/fraction.js": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
"integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.1.tgz",
"integrity": "sha512-nx0cki48JBA6ThPeUpeKCNpdhEl/9bRS+dAEYnRUod+Z1jhFfC3K/mBLorZZntqHM+GTH3/dkkpfoT3QITYe7g==",
"dev": true,
"engines": {
"node": "*"
@ -14737,9 +14737,9 @@
}
},
"fraction.js": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz",
"integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.1.tgz",
"integrity": "sha512-nx0cki48JBA6ThPeUpeKCNpdhEl/9bRS+dAEYnRUod+Z1jhFfC3K/mBLorZZntqHM+GTH3/dkkpfoT3QITYe7g==",
"dev": true
},
"fs-extra": {

View file

@ -3,11 +3,11 @@
"version": "0.1.2",
"private": true,
"dependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@headlessui/react": "^1.7.10",
"@heroicons/react": "^2.0.15",
"@mui/material": "^5.11.9",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"@mui/material": "^5.14.7",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
@ -24,20 +24,20 @@
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.6",
"@tabler/icons-react": "^2.18.0",
"@tailwindcss/forms": "^0.5.3",
"@tabler/icons-react": "^2.32.0",
"@tailwindcss/forms": "^0.5.6",
"@tailwindcss/line-clamp": "^0.4.4",
"@types/axios": "^0.14.0",
"accordion": "^3.0.2",
"ace-builds": "^1.16.0",
"ace-builds": "^1.24.1",
"add": "^2.0.6",
"ansi-to-html": "^0.7.2",
"axios": "^1.3.2",
"axios": "^1.5.0",
"base64-js": "^1.5.1",
"class-variance-authority": "^0.6.0",
"class-variance-authority": "^0.6.1",
"clsx": "^1.2.1",
"dompurify": "^3.0.4",
"esbuild": "^0.17.18",
"dompurify": "^3.0.5",
"esbuild": "^0.17.19",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",
"moment": "^2.29.4",
@ -45,24 +45,24 @@
"react-ace": "^10.1.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.2",
"react-icons": "^4.8.0",
"react-error-boundary": "^4.0.11",
"react-icons": "^4.10.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.8.1",
"react-router-dom": "^6.15.0",
"react-syntax-highlighter": "^15.5.0",
"react-tabs": "^6.0.0",
"react-tooltip": "^5.13.1",
"react-tabs": "^6.0.2",
"react-tooltip": "^5.21.1",
"reactflow": "^11.8.3",
"rehype-mathjax": "^4.0.2",
"rehype-mathjax": "^4.0.3",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"shadcn-ui": "^0.2.2",
"shadcn-ui": "^0.2.3",
"short-unique-id": "^4.4.4",
"switch": "^0.0.0",
"table": "^6.8.1",
"tailwind-merge": "^1.13.0",
"tailwindcss-animate": "^1.0.5",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^9.0.0",
"vite-plugin-svgr": "^3.2.0",
"web-vitals": "^2.1.4"
@ -96,26 +96,26 @@
"proxy": "http://127.0.0.1:7860",
"devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.62",
"@swc/core": "^1.3.80",
"@tailwindcss/typography": "^0.5.9",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/lodash": "^4.14.194",
"@types/node": "^16.18.12",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/uuid": "^9.0.1",
"@vitejs/plugin-react-swc": "^3.0.0",
"autoprefixer": "^10.4.14",
"daisyui": "^3.1.1",
"postcss": "^8.4.23",
"@types/lodash": "^4.14.197",
"@types/node": "^16.18.46",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/uuid": "^9.0.2",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.15",
"daisyui": "^3.6.3",
"postcss": "^8.4.29",
"prettier": "^2.8.8",
"prettier-plugin-organize-imports": "^3.2.2",
"prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "^3.3.2",
"typescript": "^5.0.2",
"vite": "^4.3.9"
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2",
"vite": "^4.4.9"
}
}

View file

@ -0,0 +1,65 @@
import { useState } from "react";
import { dropdownButtonPropsType } from "../../types/components";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
export default function DropdownButton({
firstButtonName,
onFirstBtnClick,
options,
}: dropdownButtonPropsType): JSX.Element {
const [showOptions, setShowOptions] = useState<boolean>(false);
return (
<div>
<DropdownMenu open={showOptions}>
<DropdownMenuTrigger asChild>
<Button
variant="primary"
className="relative pr-10"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
onFirstBtnClick();
}}
>
{firstButtonName}
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
onPointerDownOutside={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{options.map(({ name, onBtnClick }, index) => (
<DropdownMenuItem onClick={onBtnClick} key={index}>
{name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}

View file

@ -28,7 +28,6 @@ import {
TabsList,
TabsTrigger,
} from "../../components/ui/tabs";
import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext";
import { typesContext } from "../../contexts/typesContext";
import { codeTabsPropsType } from "../../types/components";
@ -49,7 +48,6 @@ export default function CodeTabsComponent({
const [openAccordion, setOpenAccordion] = useState<string[]>([]);
const { dark } = useContext(darkContext);
const { reactFlowInstance } = useContext(typesContext);
const { isTweakPage, setIsTweakPage } = useContext(alertContext);
useEffect(() => {
if (flow && flow["data"]!["nodes"]) {
@ -58,16 +56,14 @@ export default function CodeTabsComponent({
}, [flow]);
useEffect(() => {
unselectAllNodes({
data,
updateNodes: (nodes) => {
reactFlowInstance?.setNodes(nodes);
},
});
return () => {
if (isTweakPage) setIsTweakPage(false);
};
if (tweaks) {
unselectAllNodes({
data,
updateNodes: (nodes) => {
reactFlowInstance?.setNodes(nodes);
},
});
}
}, []);
const copyToClipboard = () => {
@ -184,7 +180,7 @@ export default function CodeTabsComponent({
key={idx} // Remember to add a unique key prop
>
{idx < 4 ? (
<>
<div className="w-full h-full flex flex-col">
{tab.description && (
<div
className="mb-2 w-full text-left text-sm"
@ -198,7 +194,7 @@ export default function CodeTabsComponent({
>
{tab.code}
</SyntaxHighlighter>
</>
</div>
) : idx === 4 ? (
<>
<div className="api-modal-according-display">

View file

@ -7,8 +7,17 @@ import { alertContext } from "../../contexts/alertContext";
import { AuthContext } from "../../contexts/authContext";
import { darkContext } from "../../contexts/darkContext";
import { TabsContext } from "../../contexts/tabsContext";
import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { Separator } from "../ui/separator";
import MenuBar from "./components/menuBar";
@ -17,8 +26,8 @@ export default function Header(): JSX.Element {
const { dark, setDark } = useContext(darkContext);
const { notificationCenter } = useContext(alertContext);
const location = useLocation();
const { logout, autoLogin, isAdmin } = useContext(AuthContext);
const { stars } = useContext(darkContext);
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const { stars, gradientIndex } = useContext(darkContext);
const navigate = useNavigate();
return (
@ -31,40 +40,6 @@ export default function Header(): JSX.Element {
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
<MenuBar flows={flows} tabId={tabId} />
)}
{!autoLogin && location.pathname !== `/flow/${tabId}` && (
<a
onClick={() => {
logout();
navigate("/login");
}}
className="mx-5 cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
>
Sign out
</a>
)}
{location.pathname === "/admin" && (
<a
onClick={() => {
navigate("/");
}}
className="cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
>
Home
</a>
)}
{isAdmin &&
!autoLogin &&
location.pathname !== "/admin" &&
location.pathname !== `/flow/${tabId}` && (
<a
className="cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
onClick={() => navigate("/admin")}
>
Admin page
</a>
)}
</div>
<div className="round-button-div">
<Link to="/">
@ -156,6 +131,44 @@ export default function Header(): JSX.Element {
/>
</button>
)}
{!autoLogin && (
<>
<Separator orientation="vertical" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className={
"h-7 w-7 rounded-full focus-visible:outline-0 " +
gradients[
gradientIndex
]
}
/>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
{isAdmin && (
<DropdownMenuItem
className="cursor-pointer"
onClick={() => navigate("/admin")}
>
Admin Page
</DropdownMenuItem>
)}
<DropdownMenuItem
className="cursor-pointer"
onClick={() => {
logout();
navigate("/login");
}}
>
Sign Out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
)}
</div>
</div>
</div>

View file

@ -30,6 +30,7 @@ export default function InputComponent({
{isForm ? (
<Form.Control asChild>
<Input
type={password && !pwdVisible ? "password" : "text"}
value={value}
disabled={disabled}
required={required}
@ -53,6 +54,7 @@ export default function InputComponent({
</Form.Control>
) : (
<Input
type="text"
value={value}
disabled={disabled}
required={required}
@ -76,6 +78,8 @@ export default function InputComponent({
)}
{password && (
<button
type="button"
tabIndex={-1}
className={classNames(
editNode
? "input-component-true-button"

View file

@ -0,0 +1,16 @@
import { Skeleton } from "../ui/skeleton";
export const SkeletonCardComponent = (): JSX.Element => {
return (
<div className="skeleton-card">
<div className="skeleton-card-wrapper">
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-4 w-[120px]" />
</div>
<div className="skeleton-card-text">
<Skeleton className="h-3 w-[250px]" />
<Skeleton className="h-3 w-[200px]" />
</div>
</div>
);
};

View file

@ -27,7 +27,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-blur-shared backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
"fixed top-0 right-0 left-0 bottom-0 overflow-auto inset-0 z-50 bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"noundo nocopy fixed z-50 flex w-full flex-col gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
"noundo nocopy flex text-start overflow-auto z-50 justify-center items-left flex-col w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-bottom-[48%] data-[state=open]:slide-in-from-bottom-[48%] sm:rounded-lg md:w-full",
className
)}
{...props}

View file

@ -0,0 +1,15 @@
import { cn } from "../../utils/utils";
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-border", className)}
{...props}
/>
);
}
export { Skeleton };

View file

@ -26,8 +26,6 @@ const initialValue: alertContextType = {
pushNotificationList: () => {},
clearNotificationList: () => {},
removeFromNotificationList: () => {},
isTweakPage: false,
setIsTweakPage: () => {},
};
export const alertContext = createContext<alertContextType>(initialValue);
@ -123,8 +121,6 @@ export function AlertProvider({ children }: { children: ReactNode }) {
return (
<alertContext.Provider
value={{
isTweakPage,
setIsTweakPage,
removeFromNotificationList,
clearNotificationList,
notificationList,

View file

@ -7,6 +7,8 @@ const initialValue = {
setDark: () => {},
stars: 0,
setStars: (stars) => 0,
gradientIndex: 0,
setGradientIndex: () => 0,
};
export const darkContext = createContext<darkContextType>(initialValue);
@ -16,6 +18,7 @@ export function DarkProvider({ children }) {
JSON.parse(window.localStorage.getItem("isDark")!) ?? false
);
const [stars, setStars] = useState<number>(0);
const [gradientIndex, setGradientIndex] = useState<number>(0);
useEffect(() => {
async function fetchStars() {
@ -23,6 +26,11 @@ export function DarkProvider({ children }) {
setStars(starsCount);
}
fetchStars();
const min = 0;
const max = 30;
setGradientIndex(
Math.floor(Math.random() * (max - min + 1)) + min
)
}, []);
useEffect(() => {
@ -41,6 +49,8 @@ export function DarkProvider({ children }) {
stars,
dark,
setDark,
setGradientIndex,
gradientIndex,
}}
>
{children}

View file

@ -49,6 +49,7 @@ const TabsContextInitialValue: TabsContextType = {
save: () => {},
tabId: "",
setTabId: (index: string) => {},
isLoading: true,
flows: [],
removeFlow: (id: string) => {},
addFlow: async (flowData?: any) => "",
@ -57,7 +58,7 @@ const TabsContextInitialValue: TabsContextType = {
downloadFlow: (flow: FlowType) => {},
downloadFlows: () => {},
uploadFlows: () => {},
uploadFlow: () => {},
uploadFlow: async () => "",
isBuilt: false,
setIsBuilt: (state: boolean) => {},
hardReset: () => {},
@ -82,10 +83,12 @@ export const TabsContext = createContext<TabsContextType>(
export function TabsProvider({ children }: { children: ReactNode }) {
const { setErrorData, setNoticeData, setSuccessData } =
useContext(alertContext);
const { getAuthentication } = useContext(AuthContext);
const { getAuthentication, isAuthenticated } = useContext(AuthContext);
const [tabId, setTabId] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uid());
const { templates, reactFlowInstance } = useContext(typesContext);
@ -96,6 +99,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const [tabsState, setTabsState] = useState<TabsState>({});
const [getTweak, setTweak] = useState<tweakType>([]);
useEffect(() => {
if (!isAuthenticated) {
hardReset();
}
}, [isAuthenticated]);
const newNodeId = useRef(uid());
function incrementNodeId() {
newNodeId.current = uid();
@ -126,11 +135,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
function refreshFlows() {
setIsLoading(true);
getTabsDataFromDB().then((DbData) => {
if (DbData && Object.keys(templates).length > 0) {
try {
processDBData(DbData);
updateStateWithDbData(DbData);
setIsLoading(false);
} catch (e) {}
}
});
@ -244,6 +255,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
setTabId("");
setFlows([]);
setIsLoading(true);
setId(uid());
}
@ -301,39 +313,40 @@ export function TabsProvider({ children }: { children: ReactNode }) {
* If the file type is application/json, the file is read and parsed into a JSON object.
* The resulting JSON object is passed to the addFlow function.
*/
function uploadFlow(newProject?: boolean, file?: File) {
async function uploadFlow(
newProject?: boolean,
file?: File
): Promise<String | undefined> {
let id;
if (file) {
file.text().then((text) => {
// parse the text into a JSON object
let flow: FlowType = JSON.parse(text);
let text = await file.text();
// parse the text into a JSON object
let flow: FlowType = JSON.parse(text);
addFlow(flow, newProject);
});
id = await addFlow(flow, newProject);
} else {
// create a file input
const input = document.createElement("input");
input.type = "file";
input.accept = ".json";
// add a change event listener to the file input
input.onchange = (e: Event) => {
// check if the file type is application/json
if (
(e.target as HTMLInputElement).files![0].type === "application/json"
) {
// get the file from the file input
const currentfile = (e.target as HTMLInputElement).files![0];
// read the file as text
currentfile.text().then((text) => {
// parse the text into a JSON object
id = await new Promise((resolve) => {
input.onchange = async (e: Event) => {
if (
(e.target as HTMLInputElement).files![0].type === "application/json"
) {
const currentfile = (e.target as HTMLInputElement).files![0];
let text = await currentfile.text();
let flow: FlowType = JSON.parse(text);
addFlow(flow, newProject);
});
}
};
// trigger the file input click event to open the file dialog
input.click();
const flowId = await addFlow(flow, newProject);
resolve(flowId);
}
};
// trigger the file input click event to open the file dialog
input.click();
});
}
return id;
}
function uploadFlows() {
@ -665,6 +678,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
paste,
getTweak,
setTweak,
isLoading,
}}
>
{children}

View file

@ -33,7 +33,7 @@ function ApiInterceptor() {
}
const res = await renewAccessToken(refreshToken);
login(res.data.access_token, res.data.refresh_token);
login(res?.data?.access_token, res?.data?.refresh_token);
try {
if (error?.config?.headers) {
delete error.config.headers["Authorization"];

View file

@ -164,7 +164,7 @@ export async function readFlowsFromDatabase() {
try {
const response = await api.get(`${BASE_URL_API}flows/`);
if (response?.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`);
throw new Error(`HTTP error! status: ${response?.status}`);
}
return response.data;
} catch (error) {
@ -177,7 +177,7 @@ export async function downloadFlowsFromDatabase() {
try {
const response = await api.get(`${BASE_URL_API}flows/download/`);
if (response?.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`);
throw new Error(`HTTP error! status: ${response?.status}`);
}
return response.data;
} catch (error) {
@ -190,8 +190,8 @@ export async function uploadFlowsToDatabase(flows: FormData) {
try {
const response = await api.post(`${BASE_URL_API}flows/upload/`, flows);
if (response.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`);
if (response?.status !== 201) {
throw new Error(`HTTP error! status: ${response?.status}`);
}
return response.data;
} catch (error) {
@ -312,7 +312,7 @@ export async function getHealth() {
*/
export async function getBuildStatus(
flowId: string
): Promise<BuildStatusTypeAPI> {
): Promise<AxiosResponse<BuildStatusTypeAPI>> {
return await api.get(`${BASE_URL_API}build/${flowId}/status`);
}
@ -468,7 +468,7 @@ export async function updateUser(user_id: string, user: Users) {
export async function getApiKey() {
try {
const res = await api.get(`${BASE_URL_API}api_key`);
const res = await api.get(`${BASE_URL_API}api_key/`);
if (res.status === 200) {
return res.data;
}
@ -480,7 +480,7 @@ export async function getApiKey() {
export async function createApiKey(name: string) {
try {
const res = await api.post(`${BASE_URL_API}api_key`, { name });
const res = await api.post(`${BASE_URL_API}api_key/`, { name });
if (res.status === 200) {
return res.data;
}

View file

@ -25,6 +25,7 @@ import { Textarea } from "../../components/ui/textarea";
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { TabsContext } from "../../contexts/tabsContext";
import { getBuildStatus } from "../../controllers/API";
import { TabsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils";
@ -155,9 +156,23 @@ export default function FormModal({
function handleOnClose(event: CloseEvent): void {
if (isOpen.current) {
getBuildStatus(flow.id)
.then((response) => {
if (response.data.built) {
connectWS();
} else {
setErrorData({
title: "Please build the flow again before using the chat.",
});
}
})
.catch((error) => {
setErrorData({
title: error.data?.detail ? error.data.detail : error.message,
});
});
setErrorData({ title: event.reason });
setTimeout(() => {
connectWS();
setLockChat(false);
}, 1000);
}
@ -175,7 +190,7 @@ export default function FormModal({
return `${
isDevelopment ? "ws" : webSocketProtocol
}://${host}${chatEndpoint}?token=${accessToken}`;
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
}
function handleWsMessage(data: any) {
@ -260,7 +275,6 @@ export default function FormModal({
};
newWs.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received data:", data);
handleWsMessage(data);
//get chat history
};
@ -268,7 +282,6 @@ export default function FormModal({
handleOnClose(event);
};
newWs.onerror = (ev) => {
console.log(ev, "error");
if (flow.id === "") {
connectWS();
} else {
@ -294,7 +307,6 @@ export default function FormModal({
useEffect(() => {
connectWS();
return () => {
console.log("unmount");
console.log(ws);
if (ws.current) {
ws.current.close();

View file

@ -121,6 +121,7 @@ export default function GenericModal({
}
function validatePrompt(closeModal: boolean): void {
//nodeClass is always null on tweaks
postValidatePrompt(field_name, inputValue, nodeClass!)
.then((apiReturn) => {
if (apiReturn.data) {
@ -135,7 +136,11 @@ export default function GenericModal({
setSuccessData({
title: "Prompt is ready",
});
setNodeClass!(apiReturn.data?.frontend_node);
if (
JSON.stringify(apiReturn.data?.frontend_node) !==
JSON.stringify({})
)
setNodeClass!(apiReturn.data?.frontend_node);
setModalOpen(closeModal);
setValue(inputValue);
}

View file

@ -21,8 +21,7 @@ export default function ExtraSidebar(): JSX.Element {
const { data, templates } = useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
useContext(TabsContext);
const { setSuccessData, setErrorData, setIsTweakPage } =
useContext(alertContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
const isPending = tabsState[tabId]?.isPending;
@ -101,10 +100,7 @@ export default function ExtraSidebar(): JSX.Element {
<div className="side-bar-button">
{flow && flow.data && (
<ApiModal flow={flow} disable={!isBuilt}>
<div
className={classNames("extra-side-bar-buttons")}
onClick={() => setIsTweakPage(true)}
>
<div className={classNames("extra-side-bar-buttons")}>
<IconComponent
name="Code2"
className={

View file

@ -1,14 +1,33 @@
import { useContext, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import DropdownButton from "../../components/DropdownButtonComponent";
import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
import { Button } from "../../components/ui/button";
import { USER_PROJECTS_HEADER } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext";
export default function HomePage(): JSX.Element {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } =
useContext(TabsContext);
const {
flows,
setTabId,
downloadFlows,
uploadFlows,
addFlow,
removeFlow,
uploadFlow,
isLoading,
} = useContext(TabsContext);
const dropdownOptions = [
{
name: "Import from JSON",
onBtnClick: () =>
uploadFlow(true).then((id) => {
navigate("/flow/" + id);
}),
},
];
// Set a null id
useEffect(() => {
@ -16,6 +35,10 @@ export default function HomePage(): JSX.Element {
}, []);
const navigate = useNavigate();
useEffect(() => {
console.log(isLoading);
}, [isLoading]);
// Personal flows display
return (
<>
@ -45,48 +68,55 @@ export default function HomePage(): JSX.Element {
<IconComponent name="Upload" className="main-page-nav-button" />
Upload Collection
</Button>
<Button
variant="primary"
onClick={() => {
<DropdownButton
firstButtonName="New Project"
onFirstBtnClick={() => {
addFlow(null!, true).then((id) => {
navigate("/flow/" + id);
});
}}
>
<IconComponent name="Plus" className="main-page-nav-button" />
New Project
</Button>
options={dropdownOptions}
/>
</div>
</div>
<span className="main-page-description-text">
Manage your personal projects. Download or upload your collection.
</span>
<div className="main-page-flows-display">
{flows.map((flow, idx) => (
<CardComponent
key={idx}
flow={flow}
id={flow.id}
button={
<Link to={"/flow/" + flow.id}>
<Button
variant="outline"
size="sm"
className="whitespace-nowrap "
>
<IconComponent
name="ExternalLink"
className="main-page-nav-button"
/>
Edit Flow
</Button>
</Link>
}
onDelete={() => {
removeFlow(flow.id);
}}
/>
))}
{isLoading && flows.length == 0 ? (
<>
<SkeletonCardComponent />
<SkeletonCardComponent />
<SkeletonCardComponent />
<SkeletonCardComponent />
</>
) : (
flows.map((flow, idx) => (
<CardComponent
key={idx}
flow={flow}
id={flow.id}
button={
<Link to={"/flow/" + flow.id}>
<Button
variant="outline"
size="sm"
className="whitespace-nowrap "
>
<IconComponent
name="ExternalLink"
className="main-page-nav-button"
/>
Edit Flow
</Button>
</Link>
}
onDelete={() => {
removeFlow(flow.id);
}}
/>
))
)}
</div>
</div>
</>

View file

@ -32,8 +32,8 @@ export default function LoginPage(): JSX.Element {
function signIn() {
const user: LoginType = {
username: username,
password: password,
username: username.trim(),
password: password.trim(),
};
onLogin(user)
.then((user) => {
@ -90,6 +90,7 @@ export default function LoginPage(): JSX.Element {
<Form.Control asChild>
<Input
type="username"
onChange={({ target: { value } }) => {
handleInput({ target: { name: "username", value } });
}}
@ -130,12 +131,14 @@ export default function LoginPage(): JSX.Element {
</div>
<div className="w-full">
<Form.Submit asChild>
<Button className="mr-3 mt-6 w-full">Sign in</Button>
<Button className="mr-3 mt-6 w-full" type="submit">
Sign in
</Button>
</Form.Submit>
</div>
<div className="w-full">
<Link to="/signup">
<Button className="w-full" variant="outline">
<Button className="w-full" variant="outline" type="button">
Don't have an account?&nbsp;<b>Sign Up</b>
</Button>
</Link>

View file

@ -33,8 +33,8 @@ export default function SignUp(): JSX.Element {
function handleSignup(): void {
const { username, password } = inputState;
const newUser: UserInputType = {
username,
password,
username: username.trim(),
password: password.trim(),
};
addUser(newUser)
.then((user) => {
@ -84,6 +84,7 @@ export default function SignUp(): JSX.Element {
<Form.Control asChild>
<Input
type="username"
onChange={({ target: { value } }) => {
handleInput({ target: { name: "username", value } });
}}
@ -157,6 +158,7 @@ export default function SignUp(): JSX.Element {
<div className="w-full">
<Form.Submit asChild>
<Button
type="submit"
className="mr-3 mt-6 w-full"
onClick={() => {
handleSignup();

View file

@ -126,6 +126,18 @@
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
}
.skeleton-card {
@apply bg-background h-48 p-4 border rounded-lg flex flex-col gap-6;
}
.skeleton-card-wrapper {
@apply flex items-center space-x-4;
}
.skeleton-card-text {
@apply flex flex-col gap-3;
}
/* The same as primary-input but no-truncate */
.textarea-primary {
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;

View file

@ -36,3 +36,18 @@ pre {
.gradient-start {
animation: gradient-motion-start 4s infinite forwards;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
-webkit-text-fill-color: black;
-webkit-box-shadow: 0 0 0px 1000px #fff6d0 inset;
box-shadow: 0 0 0px 1000px #fff6d0 inset;
color: black;
}

View file

@ -544,3 +544,9 @@ export type fetchErrorComponentType = {
message: string;
description: string;
};
export type dropdownButtonPropsType = {
firstButtonName: string;
onFirstBtnClick: () => void;
options: Array<{ name: string; onBtnClick: () => void }>;
};

View file

@ -5,6 +5,7 @@ export type TabsContextType = {
saveFlow: (flow: FlowType) => Promise<void>;
save: () => void;
tabId: string;
isLoading: boolean;
setTabId: (index: string) => void;
flows: Array<FlowType>;
removeFlow: (id: string) => void;
@ -23,7 +24,7 @@ export type TabsContextType = {
uploadFlows: () => void;
isBuilt: boolean;
setIsBuilt: (state: boolean) => void;
uploadFlow: (newFlow?: boolean, file?: File) => void;
uploadFlow: (newFlow?: boolean, file?: File) => Promise<String | undefined>;
hardReset: () => void;
getNodeId: (nodeType: string) => string;
tabsState: TabsState;

View file

@ -41,8 +41,6 @@ export type alertContextType = {
removeFromNotificationList: (index: string) => void;
loading: boolean;
setLoading: (newState: boolean) => void;
isTweakPage: boolean;
setIsTweakPage: (newState: boolean) => void;
};
export type darkContextType = {
@ -50,6 +48,8 @@ export type darkContextType = {
setDark: (newState: {}) => void;
stars: number;
setStars: (stars: number) => void;
gradientIndex: number;
setGradientIndex: (index: number) => void;
};
export type locationContextType = {

View file

@ -5,6 +5,7 @@ import {
ChevronDown,
ChevronLeft,
ChevronRight,
ChevronUp,
ChevronsLeft,
ChevronsRight,
ChevronsUpDown,
@ -302,4 +303,5 @@ export const nodeIconsLucide: iconsType = {
Key,
Unplug,
Group
ChevronUp,
};

View file

@ -9,7 +9,13 @@ from langflow.services.database.models.user import UserUpdate
@pytest.fixture
def super_user(client, session):
return create_super_user(session)
settings_manager = get_settings_manager()
auth_settings = settings_manager.auth_settings
return create_super_user(
db=session,
username=auth_settings.FIRST_SUPERUSER,
password=auth_settings.FIRST_SUPERUSER_PASSWORD,
)
@pytest.fixture