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

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

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

View file

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

234
poetry.lock generated
View file

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

View file

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

View file

@ -356,7 +356,7 @@ def superuser(
with session_getter(db_manager) as session: with session_getter(db_manager) as session:
from langflow.services.auth.utils import create_super_user 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 # Verify that the superuser was created
from langflow.services.database.models.user.user import User from langflow.services.database.models.user.user import User

View file

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

View file

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

View file

@ -10,7 +10,7 @@ from fastapi import WebSocket
from langchain.schema import AgentAction, LLMResult, AgentFinish 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 # https://github.com/hwchase17/chat-langchain/blob/master/callback.py

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@ from langflow.api.v1.base import (
validate_prompt, validate_prompt,
) )
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.validate import validate_code from langflow.utils.validate import validate_code
# build router # build router
@ -31,7 +31,12 @@ def post_validate_code(code: Code):
def post_validate_prompt(prompt_request: ValidatePromptRequest): def post_validate_prompt(prompt_request: ValidatePromptRequest):
try: try:
input_variables = validate_prompt(prompt_request.template) 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) old_custom_fields = get_old_custom_fields(prompt_request)
add_new_variables_to_template(input_variables, prompt_request) add_new_variables_to_template(input_variables, prompt_request)

View file

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

View file

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

View file

@ -3,7 +3,7 @@ from langflow.graph.utils import UnbuiltObject
from langflow.interface.initialize import loading from langflow.interface.initialize import loading
from langflow.interface.listing import lazy_load_dict from langflow.interface.listing import lazy_load_dict
from langflow.utils.constants import DIRECT_TYPES 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 from langflow.utils.util import sync_to_async

View file

@ -8,7 +8,7 @@ from langflow.interface.base import LangChainTypeCreator
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.agents import AgentFrontendNode 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 from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -8,7 +8,7 @@ from pydantic import BaseModel
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.template.base import Template 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 # Assuming necessary imports for Field, Template, and FrontendNode classes

View file

@ -6,7 +6,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.chains import ChainFrontendNode 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 langflow.utils.util import build_template_from_class, build_template_from_method
from langchain import chains from langchain import chains
from langchain_experimental.sql import SQLDatabaseChain # type: ignore from langchain_experimental.sql import SQLDatabaseChain # type: ignore

View file

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

View file

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

View file

@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict 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 from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode 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 from langflow.utils.util import build_template_from_class

View file

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

View file

@ -5,7 +5,7 @@ from langflow.interface.custom_lists import llm_type_to_cls_dict
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.llms import LLMFrontendNode 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 from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.memories import MemoryFrontendNode 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.utils.util import build_template_from_class, build_template_from_method
from langflow.custom.customs import get_custom_nodes from langflow.custom.customs import get_custom_nodes

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode 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 from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.prompts import PromptFrontendNode 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 from langflow.utils.util import build_template_from_class

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.retrievers import RetrieverFrontendNode 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 from langflow.utils.util import build_template_from_method, build_template_from_class

View file

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

View file

@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict 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 from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.interface.base import LangChainTypeCreator
from langflow.interface.importing.utils import import_class, import_module from langflow.interface.importing.utils import import_class, import_module
from langflow.services.utils import get_settings_manager 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 from langflow.utils.util import build_template_from_class

View file

@ -3,7 +3,7 @@ import inspect
from typing import Dict, Union from typing import Dict, Union
from langchain.agents.tools import Tool 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]: def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:

View file

@ -29,7 +29,7 @@ from langflow.template.frontend_node.custom_components import (
from langflow.interface.retrievers.base import retriever_creator from langflow.interface.retrievers.base import retriever_creator
from langflow.interface.custom.directory_reader import DirectoryReader 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 from langflow.utils.util import get_base_classes
import re import re

View file

@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode 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 from langflow.utils.util import build_template_from_class

View file

@ -8,7 +8,7 @@ import re
import yaml import yaml
from langchain.base_language import BaseLanguageModel from langchain.base_language import BaseLanguageModel
from PIL.Image import Image 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.chat.config import ChatConfig
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode 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 from langflow.utils.util import build_template_from_method

View file

@ -3,7 +3,7 @@ from typing import Dict, List, Optional
from langchain import requests, sql_database from langchain import requests, sql_database
from langflow.interface.base import LangChainTypeCreator 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 from langflow.utils.util import build_template_from_class, build_template_from_method

View file

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

View file

@ -4,7 +4,7 @@ from langflow.api.v1.callback import (
StreamingLLMCallbackHandler, StreamingLLMCallbackHandler,
) )
from langflow.processing.process import fix_memory_inputs, format_actions 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 from langchain.agents.agent import AgentExecutor

View file

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

View file

@ -37,7 +37,12 @@ async def api_key_security(
result: Optional[Union[ApiKey, User]] = None result: Optional[Union[ApiKey, User]] = None
if settings_manager.auth_settings.AUTO_LOGIN: if settings_manager.auth_settings.AUTO_LOGIN:
# Get the first user # 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( result = get_user_by_username(
db, settings_manager.auth_settings.FIRST_SUPERUSER db, settings_manager.auth_settings.FIRST_SUPERUSER
) )
@ -80,6 +85,9 @@ async def get_current_user(
if isinstance(token, Coroutine): if isinstance(token, Coroutine):
token = await token token = await token
if settings_manager.auth_settings.SECRET_KEY is None:
raise credentials_exception
try: try:
payload = jwt.decode( payload = jwt.decode(
token, token,
@ -88,6 +96,11 @@ async def get_current_user(
) )
user_id: UUID = payload.get("sub") # type: ignore user_id: UUID = payload.get("sub") # type: ignore
token_type: str = payload.get("type") # 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: if user_id is None or token_type:
raise credentials_exception raise credentials_exception
@ -145,22 +158,16 @@ def create_token(data: dict, expires_delta: timedelta):
def create_super_user( def create_super_user(
username: str,
password: str,
db: Session = Depends(get_session), db: Session = Depends(get_session),
username: Optional[str] = None,
password: Optional[str] = None,
) -> User: ) -> User:
settings_manager = get_settings_manager() super_user = get_user_by_username(db, username)
super_user = get_user_by_username(
db, username or settings_manager.auth_settings.FIRST_SUPERUSER
)
if not super_user: if not super_user:
super_user = User( super_user = User(
username=username or settings_manager.auth_settings.FIRST_SUPERUSER, username=username,
password=get_password_hash( password=get_password_hash(password),
password or settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
),
is_superuser=True, is_superuser=True,
is_active=True, is_active=True,
last_login_at=None, last_login_at=None,
@ -174,7 +181,15 @@ def create_super_user(
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict: 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_expires_longterm = timedelta(days=365)
access_token = create_token( access_token = create_token(

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,13 +1,22 @@
from pathlib import Path
from typing import Optional from typing import Optional
import secrets 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 passlib.context import CryptContext
from loguru import logger
class AuthSettings(BaseSettings): class AuthSettings(BaseSettings):
# Login settings # 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" ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
REFRESH_TOKEN_EXPIRE_MINUTES: int = 70 REFRESH_TOKEN_EXPIRE_MINUTES: int = 70
@ -31,3 +40,33 @@ class AuthSettings(BaseSettings):
validate_assignment = True validate_assignment = True
extra = "ignore" extra = "ignore"
env_prefix = "LANGFLOW_" env_prefix = "LANGFLOW_"
@validator("SECRET_KEY", pre=True)
def get_secret_key(cls, value, values):
config_dir = values.get("CONFIG_DIR")
if not config_dir:
logger.debug("No CONFIG_DIR provided, not saving secret key")
return value or secrets.token_urlsafe(32)
secret_key_path = Path(config_dir) / "secret_key"
if value:
logger.debug("Secret key provided")
write_secret_to_file(secret_key_path, value)
else:
logger.debug("No secret key provided, generating a random one")
if secret_key_path.exists():
value = read_secret_from_file(secret_key_path)
logger.debug("Loaded secret key")
if not value:
value = secrets.token_urlsafe(32)
write_secret_to_file(secret_key_path, value)
logger.debug("Saved secret key")
else:
value = secrets.token_urlsafe(32)
write_secret_to_file(secret_key_path, value)
logger.debug("Saved secret key")
return value

View file

@ -8,7 +8,7 @@ from pathlib import Path
import yaml import yaml
from pydantic import BaseSettings, root_validator, validator 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 / "components")
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components") BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")

View file

@ -1,7 +1,7 @@
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.settings.auth import AuthSettings from langflow.services.settings.auth import AuthSettings
from langflow.services.settings.base import Settings from langflow.services.settings.base import Settings
from langflow.utils.logger import logger from loguru import logger
import os import os
import yaml import yaml
@ -35,5 +35,10 @@ class SettingsManager(Service):
) )
settings = Settings(**settings_dict) 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) return cls(settings, auth_settings)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -126,6 +126,18 @@
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm; @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 */ /* The same as primary-input but no-truncate */
.textarea-primary { .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; @apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,13 @@ from langflow.services.database.models.user import UserUpdate
@pytest.fixture @pytest.fixture
def super_user(client, session): 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 @pytest.fixture