diff --git a/.githooks/pre-commit b/.githooks/pre-commit old mode 100644 new mode 100755 diff --git a/Makefile b/Makefile index 59dd4a82d..59f326853 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/poetry.lock b/poetry.lock index d75538484..c2a290db4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -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" diff --git a/pyproject.toml b/pyproject.toml index a70fa6c83..077a0cb39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index b5ec034de..a08ae9fb0 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -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 diff --git a/src/backend/langflow/alembic/versions/260dbcc8b680_adds_tables.py b/src/backend/langflow/alembic/versions/260dbcc8b680_adds_tables.py index 221e66933..53e049a05 100644 --- a/src/backend/langflow/alembic/versions/260dbcc8b680_adds_tables.py +++ b/src/backend/langflow/alembic/versions/260dbcc8b680_adds_tables.py @@ -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 = [] diff --git a/src/backend/langflow/api/v1/base.py b/src/backend/langflow/api/v1/base.py index 71cac5412..f2c2f3f59 100644 --- a/src/backend/langflow/api/v1/base.py +++ b/src/backend/langflow/api/v1/base.py @@ -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 = { diff --git a/src/backend/langflow/api/v1/callback.py b/src/backend/langflow/api/v1/callback.py index 69dbf5082..2a16a0bd2 100644 --- a/src/backend/langflow/api/v1/callback.py +++ b/src/backend/langflow/api/v1/callback.py @@ -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 diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index 83d85b719..2b3426915 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -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) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 813aaf415..d1f898105 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -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}" diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index b215b9f95..be65048d4 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -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(): diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index afe67a916..4241b8d47 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -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) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index 5094409cb..517dd7f69 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -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 diff --git a/src/backend/langflow/api/v1/validate.py b/src/backend/langflow/api/v1/validate.py index 2a5bdd673..457db5bd3 100644 --- a/src/backend/langflow/api/v1/validate.py +++ b/src/backend/langflow/api/v1/validate.py @@ -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) diff --git a/src/backend/langflow/graph/edge/base.py b/src/backend/langflow/graph/edge/base.py index aa742bd63..b199efc0c 100644 --- a/src/backend/langflow/graph/edge/base.py +++ b/src/backend/langflow/graph/edge/base.py @@ -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 diff --git a/src/backend/langflow/graph/graph/base.py b/src/backend/langflow/graph/graph/base.py index 2b22d352c..94964e472 100644 --- a/src/backend/langflow/graph/graph/base.py +++ b/src/backend/langflow/graph/graph/base.py @@ -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 diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index d5c4beed9..0f9a5e8a9 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -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 diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index ec8c42aba..574264e47 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -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 diff --git a/src/backend/langflow/interface/base.py b/src/backend/langflow/interface/base.py index d1ed83b5a..b006a3174 100644 --- a/src/backend/langflow/interface/base.py +++ b/src/backend/langflow/interface/base.py @@ -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 diff --git a/src/backend/langflow/interface/chains/base.py b/src/backend/langflow/interface/chains/base.py index b906dbd25..755ac82dd 100644 --- a/src/backend/langflow/interface/chains/base.py +++ b/src/backend/langflow/interface/chains/base.py @@ -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 diff --git a/src/backend/langflow/interface/custom/base.py b/src/backend/langflow/interface/custom/base.py index 06e874fa7..45a3ea215 100644 --- a/src/backend/langflow/interface/custom/base.py +++ b/src/backend/langflow/interface/custom/base.py @@ -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 diff --git a/src/backend/langflow/interface/custom/directory_reader.py b/src/backend/langflow/interface/custom/directory_reader.py index 44b2d4f1b..01b11a4a6 100644 --- a/src/backend/langflow/interface/custom/directory_reader.py +++ b/src/backend/langflow/interface/custom/directory_reader.py @@ -1,7 +1,7 @@ import os import ast import zlib -from langflow.utils.logger import logger +from loguru import logger class CustomComponentPathValueError(ValueError): diff --git a/src/backend/langflow/interface/document_loaders/base.py b/src/backend/langflow/interface/document_loaders/base.py index db0832ff3..a2c147e16 100644 --- a/src/backend/langflow/interface/document_loaders/base.py +++ b/src/backend/langflow/interface/document_loaders/base.py @@ -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 diff --git a/src/backend/langflow/interface/embeddings/base.py b/src/backend/langflow/interface/embeddings/base.py index 169985d37..1063d10d1 100644 --- a/src/backend/langflow/interface/embeddings/base.py +++ b/src/backend/langflow/interface/embeddings/base.py @@ -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 diff --git a/src/backend/langflow/interface/initialize/loading.py b/src/backend/langflow/interface/initialize/loading.py index 589d4b3ff..b600fb5b2 100644 --- a/src/backend/langflow/interface/initialize/loading.py +++ b/src/backend/langflow/interface/initialize/loading.py @@ -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 diff --git a/src/backend/langflow/interface/llms/base.py b/src/backend/langflow/interface/llms/base.py index f562b99ed..87e4937cf 100644 --- a/src/backend/langflow/interface/llms/base.py +++ b/src/backend/langflow/interface/llms/base.py @@ -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 diff --git a/src/backend/langflow/interface/memories/base.py b/src/backend/langflow/interface/memories/base.py index 70665602c..61c6cc430 100644 --- a/src/backend/langflow/interface/memories/base.py +++ b/src/backend/langflow/interface/memories/base.py @@ -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 diff --git a/src/backend/langflow/interface/output_parsers/base.py b/src/backend/langflow/interface/output_parsers/base.py index 256b521e1..b6eb36a0e 100644 --- a/src/backend/langflow/interface/output_parsers/base.py +++ b/src/backend/langflow/interface/output_parsers/base.py @@ -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 diff --git a/src/backend/langflow/interface/prompts/base.py b/src/backend/langflow/interface/prompts/base.py index 5aa41dfb2..70818429e 100644 --- a/src/backend/langflow/interface/prompts/base.py +++ b/src/backend/langflow/interface/prompts/base.py @@ -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 diff --git a/src/backend/langflow/interface/retrievers/base.py b/src/backend/langflow/interface/retrievers/base.py index db1cfd165..92e3f2f61 100644 --- a/src/backend/langflow/interface/retrievers/base.py +++ b/src/backend/langflow/interface/retrievers/base.py @@ -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 diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 42cea0e98..9d3d95cda 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -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) diff --git a/src/backend/langflow/interface/text_splitters/base.py b/src/backend/langflow/interface/text_splitters/base.py index 87b778c4c..8b21303ce 100644 --- a/src/backend/langflow/interface/text_splitters/base.py +++ b/src/backend/langflow/interface/text_splitters/base.py @@ -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 diff --git a/src/backend/langflow/interface/toolkits/base.py b/src/backend/langflow/interface/toolkits/base.py index c13ffdbd9..fe0003b15 100644 --- a/src/backend/langflow/interface/toolkits/base.py +++ b/src/backend/langflow/interface/toolkits/base.py @@ -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 diff --git a/src/backend/langflow/interface/tools/util.py b/src/backend/langflow/interface/tools/util.py index 38276c620..f92386f18 100644 --- a/src/backend/langflow/interface/tools/util.py +++ b/src/backend/langflow/interface/tools/util.py @@ -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]: diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index 824b0af50..47743560e 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -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 diff --git a/src/backend/langflow/interface/utilities/base.py b/src/backend/langflow/interface/utilities/base.py index eb8cd60af..9009983b0 100644 --- a/src/backend/langflow/interface/utilities/base.py +++ b/src/backend/langflow/interface/utilities/base.py @@ -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 diff --git a/src/backend/langflow/interface/utils.py b/src/backend/langflow/interface/utils.py index 1fddbf80f..75e854e16 100644 --- a/src/backend/langflow/interface/utils.py +++ b/src/backend/langflow/interface/utils.py @@ -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 diff --git a/src/backend/langflow/interface/vector_store/base.py b/src/backend/langflow/interface/vector_store/base.py index 4b8ca2b64..f7aca8c9c 100644 --- a/src/backend/langflow/interface/vector_store/base.py +++ b/src/backend/langflow/interface/vector_store/base.py @@ -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 diff --git a/src/backend/langflow/interface/wrappers/base.py b/src/backend/langflow/interface/wrappers/base.py index 77e38f921..c4399fb3e 100644 --- a/src/backend/langflow/interface/wrappers/base.py +++ b/src/backend/langflow/interface/wrappers/base.py @@ -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 diff --git a/src/backend/langflow/main.py b/src/backend/langflow/main.py index 57f3e34dc..f567e65bb 100644 --- a/src/backend/langflow/main.py +++ b/src/backend/langflow/main.py @@ -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 diff --git a/src/backend/langflow/processing/base.py b/src/backend/langflow/processing/base.py index 13ff6a385..c3d766f15 100644 --- a/src/backend/langflow/processing/base.py +++ b/src/backend/langflow/processing/base.py @@ -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 diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 4b2e7b178..c60a53de5 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -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 diff --git a/src/backend/langflow/services/auth/utils.py b/src/backend/langflow/services/auth/utils.py index a8e3e1790..485968a38 100644 --- a/src/backend/langflow/services/auth/utils.py +++ b/src/backend/langflow/services/auth/utils.py @@ -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( diff --git a/src/backend/langflow/services/base.py b/src/backend/langflow/services/base.py index 6bca6c4e2..aaa966047 100644 --- a/src/backend/langflow/services/base.py +++ b/src/backend/langflow/services/base.py @@ -1,2 +1,8 @@ -class Service: +from abc import ABC + + +class Service(ABC): name: str + + def teardown(self): + pass diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index afc004d2d..d8492e961 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -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): diff --git a/src/backend/langflow/services/chat/utils.py b/src/backend/langflow/services/chat/utils.py index 17c976eb9..d9c291757 100644 --- a/src/backend/langflow/services/chat/utils.py +++ b/src/backend/langflow/services/chat/utils.py @@ -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( diff --git a/src/backend/langflow/services/database/manager.py b/src/backend/langflow/services/database/manager.py index 2b599a0ba..7f8afab6f 100644 --- a/src/backend/langflow/services/database/manager.py +++ b/src/backend/langflow/services/database/manager.py @@ -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() diff --git a/src/backend/langflow/services/database/utils.py b/src/backend/langflow/services/database/utils.py index e6afae184..fd0a8856a 100644 --- a/src/backend/langflow/services/database/utils.py +++ b/src/backend/langflow/services/database/utils.py @@ -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 diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index e9895adab..60a93fe16 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -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() diff --git a/src/backend/langflow/services/settings/auth.py b/src/backend/langflow/services/settings/auth.py index ce1bfe108..d1f8197f0 100644 --- a/src/backend/langflow/services/settings/auth.py +++ b/src/backend/langflow/services/settings/auth.py @@ -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 diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py index 00cd2085f..366ff474d 100644 --- a/src/backend/langflow/services/settings/base.py +++ b/src/backend/langflow/services/settings/base.py @@ -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") diff --git a/src/backend/langflow/services/settings/manager.py b/src/backend/langflow/services/settings/manager.py index 1a6c0feeb..2d687d784 100644 --- a/src/backend/langflow/services/settings/manager.py +++ b/src/backend/langflow/services/settings/manager.py @@ -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) diff --git a/src/backend/langflow/services/settings/utils.py b/src/backend/langflow/services/settings/utils.py new file mode 100644 index 000000000..fae96ff28 --- /dev/null +++ b/src/backend/langflow/services/settings/utils.py @@ -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() diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index 6860f8928..8b32aef02 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -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) diff --git a/src/backend/langflow/utils/logger.py b/src/backend/langflow/utils/logger.py index deb0f75ca..1f616486b 100644 --- a/src/backend/langflow/utils/logger.py +++ b/src/backend/langflow/utils/logger.py @@ -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 = "{time:HH:mm:ss} - {level: <8} - {message}" + 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}") diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 65657cd73..2c1ecabfa 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -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": { diff --git a/src/frontend/package.json b/src/frontend/package.json index bd6e031df..8d7d25d88 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -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" } } diff --git a/src/frontend/src/components/DropdownButtonComponent/index.tsx b/src/frontend/src/components/DropdownButtonComponent/index.tsx new file mode 100644 index 000000000..784a08528 --- /dev/null +++ b/src/frontend/src/components/DropdownButtonComponent/index.tsx @@ -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(false); + + return ( + + + + { + event.stopPropagation(); + event.preventDefault(); + onFirstBtnClick(); + }} + > + {firstButtonName} + { + event.stopPropagation(); + event.preventDefault(); + setShowOptions(!showOptions); + }} + > + {!showOptions ? ( + + ) : ( + + )} + + + + { + event.stopPropagation(); + event.preventDefault(); + setShowOptions(!showOptions); + }} + > + {options.map(({ name, onBtnClick }, index) => ( + + {name} + + ))} + + + + ); +} diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index fcd7385d7..9868b709a 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -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([]); 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 ? ( - <> + {tab.description && ( {tab.code} - > + ) : idx === 4 ? ( <> diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index 039b8895c..2d63d0ecf 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -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 !== "" && ( )} - {!autoLogin && location.pathname !== `/flow/${tabId}` && ( - { - logout(); - navigate("/login"); - }} - className="mx-5 cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary" - > - Sign out - - )} - - {location.pathname === "/admin" && ( - { - navigate("/"); - }} - className="cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary" - > - Home - - )} - - {isAdmin && - !autoLogin && - location.pathname !== "/admin" && - location.pathname !== `/flow/${tabId}` && ( - navigate("/admin")} - > - Admin page - - )} @@ -156,6 +131,44 @@ export default function Header(): JSX.Element { /> )} + {!autoLogin && ( + <> + + + + + + + My Account + + {isAdmin && ( + navigate("/admin")} + > + Admin Page + + )} + { + logout(); + navigate("/login"); + }} + > + Sign Out + + + + > + )} diff --git a/src/frontend/src/components/inputComponent/index.tsx b/src/frontend/src/components/inputComponent/index.tsx index 987c53a8f..490be0fb0 100644 --- a/src/frontend/src/components/inputComponent/index.tsx +++ b/src/frontend/src/components/inputComponent/index.tsx @@ -30,6 +30,7 @@ export default function InputComponent({ {isForm ? ( ) : ( { + return ( + + + + + + + + + + + ); +}; diff --git a/src/frontend/src/components/ui/dialog.tsx b/src/frontend/src/components/ui/dialog.tsx index 57dda0fef..93680ce7e 100644 --- a/src/frontend/src/components/ui/dialog.tsx +++ b/src/frontend/src/components/ui/dialog.tsx @@ -27,7 +27,7 @@ const DialogOverlay = React.forwardRef< ) { + return ( + + ); +} + +export { Skeleton }; diff --git a/src/frontend/src/contexts/alertContext.tsx b/src/frontend/src/contexts/alertContext.tsx index 98b2fdef8..0b183a297 100644 --- a/src/frontend/src/contexts/alertContext.tsx +++ b/src/frontend/src/contexts/alertContext.tsx @@ -26,8 +26,6 @@ const initialValue: alertContextType = { pushNotificationList: () => {}, clearNotificationList: () => {}, removeFromNotificationList: () => {}, - isTweakPage: false, - setIsTweakPage: () => {}, }; export const alertContext = createContext(initialValue); @@ -123,8 +121,6 @@ export function AlertProvider({ children }: { children: ReactNode }) { return ( {}, stars: 0, setStars: (stars) => 0, + gradientIndex: 0, + setGradientIndex: () => 0, }; export const darkContext = createContext(initialValue); @@ -16,6 +18,7 @@ export function DarkProvider({ children }) { JSON.parse(window.localStorage.getItem("isDark")!) ?? false ); const [stars, setStars] = useState(0); + const [gradientIndex, setGradientIndex] = useState(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} diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 9b118afb1..c2d684c6e 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -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( 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>([]); const [id, setId] = useState(uid()); const { templates, reactFlowInstance } = useContext(typesContext); @@ -96,6 +99,12 @@ export function TabsProvider({ children }: { children: ReactNode }) { const [tabsState, setTabsState] = useState({}); const [getTweak, setTweak] = useState([]); + 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 { + 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} diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index 59fba4393..4c444ff37 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -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"]; diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 3e6c78801..bdf7e5221 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -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 { +): Promise> { 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; } diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx index 01d6a0954..2982b8738 100644 --- a/src/frontend/src/modals/formModal/index.tsx +++ b/src/frontend/src/modals/formModal/index.tsx @@ -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(); diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx index e9c3e5e8b..b29c5acf8 100644 --- a/src/frontend/src/modals/genericModal/index.tsx +++ b/src/frontend/src/modals/genericModal/index.tsx @@ -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); } diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index c506975ae..8ea74390a 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -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 { {flow && flow.data && ( - setIsTweakPage(true)} - > + + 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 { Upload Collection - { + { addFlow(null!, true).then((id) => { navigate("/flow/" + id); }); }} - > - - New Project - + options={dropdownOptions} + /> Manage your personal projects. Download or upload your collection. - {flows.map((flow, idx) => ( - - - - Edit Flow - - - } - onDelete={() => { - removeFlow(flow.id); - }} - /> - ))} + {isLoading && flows.length == 0 ? ( + <> + + + + + > + ) : ( + flows.map((flow, idx) => ( + + + + Edit Flow + + + } + onDelete={() => { + removeFlow(flow.id); + }} + /> + )) + )} > diff --git a/src/frontend/src/pages/loginPage/index.tsx b/src/frontend/src/pages/loginPage/index.tsx index a3ac0fbcd..061d4ac0b 100644 --- a/src/frontend/src/pages/loginPage/index.tsx +++ b/src/frontend/src/pages/loginPage/index.tsx @@ -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 { { handleInput({ target: { name: "username", value } }); }} @@ -130,12 +131,14 @@ export default function LoginPage(): JSX.Element { - Sign in + + Sign in + - + Don't have an account? Sign Up diff --git a/src/frontend/src/pages/signUpPage/index.tsx b/src/frontend/src/pages/signUpPage/index.tsx index 92f3eff97..4b83bc07d 100644 --- a/src/frontend/src/pages/signUpPage/index.tsx +++ b/src/frontend/src/pages/signUpPage/index.tsx @@ -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 { { handleInput({ target: { name: "username", value } }); }} @@ -157,6 +158,7 @@ export default function SignUp(): JSX.Element { { handleSignup(); diff --git a/src/frontend/src/style/applies.css b/src/frontend/src/style/applies.css index 4f6f9b3a5..685d81b05 100644 --- a/src/frontend/src/style/applies.css +++ b/src/frontend/src/style/applies.css @@ -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; diff --git a/src/frontend/src/style/classes.css b/src/frontend/src/style/classes.css index b6662e7bf..cd5d504f2 100644 --- a/src/frontend/src/style/classes.css +++ b/src/frontend/src/style/classes.css @@ -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; +} \ No newline at end of file diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 41c6e2ab5..c12219663 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -544,3 +544,9 @@ export type fetchErrorComponentType = { message: string; description: string; }; + +export type dropdownButtonPropsType = { + firstButtonName: string; + onFirstBtnClick: () => void; + options: Array<{ name: string; onBtnClick: () => void }>; +}; diff --git a/src/frontend/src/types/tabs/index.ts b/src/frontend/src/types/tabs/index.ts index a87cdb35e..4c5b99dd7 100644 --- a/src/frontend/src/types/tabs/index.ts +++ b/src/frontend/src/types/tabs/index.ts @@ -5,6 +5,7 @@ export type TabsContextType = { saveFlow: (flow: FlowType) => Promise; save: () => void; tabId: string; + isLoading: boolean; setTabId: (index: string) => void; flows: Array; 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; hardReset: () => void; getNodeId: (nodeType: string) => string; tabsState: TabsState; diff --git a/src/frontend/src/types/typesContext/index.ts b/src/frontend/src/types/typesContext/index.ts index a7f993a7a..40f5b21ab 100644 --- a/src/frontend/src/types/typesContext/index.ts +++ b/src/frontend/src/types/typesContext/index.ts @@ -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 = { diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index d5d4af23f..9cf047006 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -5,6 +5,7 @@ import { ChevronDown, ChevronLeft, ChevronRight, + ChevronUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, @@ -302,4 +303,5 @@ export const nodeIconsLucide: iconsType = { Key, Unplug, Group + ChevronUp, }; diff --git a/tests/test_user.py b/tests/test_user.py index d734e4d61..bc617e127 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -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