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

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-08-16 19:21:47 -03:00
commit 7539ba3166
114 changed files with 6750 additions and 3275 deletions

View file

@ -32,7 +32,10 @@ lint:
poetry run ruff . --fix
install_frontend:
cd src/frontend && npm install
cd src/frontend && npm install;
install_frontendc:
cd src/frontend && npm ci;
run_frontend:
cd src/frontend && npm start
@ -40,6 +43,10 @@ run_frontend:
frontend:
make install_frontend
make run_frontend
frontendc:
make install_frontendc
make run_frontend
install_backend:
poetry install

2
package-lock.json generated
View file

@ -1,5 +1,5 @@
{
"name": "langflow",
"name": "reactFlow",
"lockfileVersion": 3,
"requires": true,
"packages": {

303
poetry.lock generated
View file

@ -165,13 +165,13 @@ tz = ["python-dateutil"]
[[package]]
name = "anthropic"
version = "0.3.8"
version = "0.3.9"
description = "Client library for the anthropic API"
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "anthropic-0.3.8-py3-none-any.whl", hash = "sha256:97ffe1bacc4214dc89b19f496cf2769746971e86f7c835a05aa21b76f260d279"},
{file = "anthropic-0.3.8.tar.gz", hash = "sha256:6651099807456c3b95b3879f5ad7d00f7e7e4f7649a2394d18032ab8be54ef16"},
{file = "anthropic-0.3.9-py3-none-any.whl", hash = "sha256:23e8daf266c707faa0b85328ada03dcf885e62c2f236eb0159352e29b9c4e2e3"},
{file = "anthropic-0.3.9.tar.gz", hash = "sha256:c19b75308c07cb1ecbea03ffcdde9ba68e5ca22a22479217712396385678bde3"},
]
[package.dependencies]
@ -180,7 +180,7 @@ distro = ">=1.7.0,<2"
httpx = ">=0.23.0,<1"
pydantic = ">=1.9.0,<2.0.0"
tokenizers = ">=0.13.0"
typing-extensions = ">=4.1.1,<5"
typing-extensions = ">=4.5,<5"
[[package]]
name = "anyio"
@ -272,13 +272,13 @@ test = ["astroid", "pytest"]
[[package]]
name = "async-timeout"
version = "4.0.2"
version = "4.0.3"
description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
]
[[package]]
@ -335,6 +335,40 @@ files = [
{file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"},
]
[[package]]
name = "bcrypt"
version = "4.0.1"
description = "Modern password hashing for your software and your servers"
optional = false
python-versions = ">=3.6"
files = [
{file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"},
{file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"},
{file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"},
{file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"},
{file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"},
{file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"},
{file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"},
{file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"},
{file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"},
{file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"},
{file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"},
{file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"},
{file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"},
{file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"},
{file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"},
{file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"},
{file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"},
{file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"},
{file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"},
{file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"},
{file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"},
]
[package.extras]
tests = ["pytest (>=3.2.1,!=3.3.0)"]
typecheck = ["mypy"]
[[package]]
name = "beautifulsoup4"
version = "4.12.2"
@ -758,13 +792,13 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"]
[[package]]
name = "cohere"
version = "4.19.2"
version = "4.20.0"
description = ""
optional = false
python-versions = ">=3.7,<4.0"
files = [
{file = "cohere-4.19.2-py3-none-any.whl", hash = "sha256:0b6a4fe04380a481a8e975ebcc9bb6433febe4d3eb583b6d6e04342a5e998345"},
{file = "cohere-4.19.2.tar.gz", hash = "sha256:a0b0fa698b3d3983fb328bb90d68fcf08faaa2268f3772ebc6bfea6ba55acf27"},
{file = "cohere-4.20.0-py3-none-any.whl", hash = "sha256:bebe4b1d21da0719aaa7db2cc180b5f3d0a802c19ad2d893d627971286e50497"},
{file = "cohere-4.20.0.tar.gz", hash = "sha256:a16e86981945c201bab67ad6d47e57f42a37bf2a774f6ac6b8b12233aae6409a"},
]
[package.dependencies]
@ -824,71 +858,63 @@ typing = ["mypy (>=0.990)"]
[[package]]
name = "coverage"
version = "7.2.7"
version = "7.3.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
{file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
{file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
{file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
{file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
{file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
{file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
{file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
{file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
{file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
{file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
{file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
{file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
{file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
{file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
{file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
{file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
{file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
{file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
{file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
{file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
{file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
{file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
{file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
{file = "coverage-7.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5"},
{file = "coverage-7.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637"},
{file = "coverage-7.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af"},
{file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1"},
{file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12"},
{file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689"},
{file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977"},
{file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51"},
{file = "coverage-7.3.0-cp310-cp310-win32.whl", hash = "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527"},
{file = "coverage-7.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1"},
{file = "coverage-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f"},
{file = "coverage-7.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d"},
{file = "coverage-7.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd"},
{file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7"},
{file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a"},
{file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74"},
{file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214"},
{file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f"},
{file = "coverage-7.3.0-cp311-cp311-win32.whl", hash = "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482"},
{file = "coverage-7.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70"},
{file = "coverage-7.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b"},
{file = "coverage-7.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446"},
{file = "coverage-7.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071"},
{file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe"},
{file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a"},
{file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873"},
{file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2"},
{file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b"},
{file = "coverage-7.3.0-cp312-cp312-win32.whl", hash = "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321"},
{file = "coverage-7.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479"},
{file = "coverage-7.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1"},
{file = "coverage-7.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd"},
{file = "coverage-7.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e"},
{file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54"},
{file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254"},
{file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0"},
{file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84"},
{file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985"},
{file = "coverage-7.3.0-cp38-cp38-win32.whl", hash = "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9"},
{file = "coverage-7.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543"},
{file = "coverage-7.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba"},
{file = "coverage-7.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393"},
{file = "coverage-7.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28"},
{file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95"},
{file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a"},
{file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34"},
{file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e"},
{file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54"},
{file = "coverage-7.3.0-cp39-cp39-win32.whl", hash = "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3"},
{file = "coverage-7.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e"},
{file = "coverage-7.3.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0"},
{file = "coverage-7.3.0.tar.gz", hash = "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865"},
]
[package.dependencies]
@ -944,13 +970,13 @@ test-randomorder = ["pytest-randomly"]
[[package]]
name = "ctransformers"
version = "0.2.21"
version = "0.2.22"
description = "Python bindings for the Transformer models implemented in C/C++ using GGML library."
optional = true
python-versions = "*"
files = [
{file = "ctransformers-0.2.21-py3-none-any.whl", hash = "sha256:18a0555d02f55a3935f5544b885038562f80e497a6197d8e871941a087dba546"},
{file = "ctransformers-0.2.21.tar.gz", hash = "sha256:58e7a699050a106688b967faa59f377886e22a581fde6cd36821dfa541995677"},
{file = "ctransformers-0.2.22-py3-none-any.whl", hash = "sha256:cce72b4ffff3f29d49ea6488110686453b3354c285e96b9c7dd16f273c4b4fc4"},
{file = "ctransformers-0.2.22.tar.gz", hash = "sha256:ffd15dfe8a6ad45568ac0423bbbb13d7d71b680a7d0b59871a193df8a4b8fdca"},
]
[package.dependencies]
@ -1275,13 +1301,13 @@ files = [
[[package]]
name = "exceptiongroup"
version = "1.1.2"
version = "1.1.3"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"},
{file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"},
{file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"},
{file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"},
]
[package.extras]
@ -1663,13 +1689,13 @@ six = "*"
[[package]]
name = "google-cloud-aiplatform"
version = "1.29.0"
version = "1.30.1"
description = "Vertex AI API client library"
optional = false
python-versions = ">=3.7"
files = [
{file = "google-cloud-aiplatform-1.29.0.tar.gz", hash = "sha256:fceabb924d2d26057e3c8c5c2e251929389aa6d553361377bc402781150c0db3"},
{file = "google_cloud_aiplatform-1.29.0-py2.py3-none-any.whl", hash = "sha256:cf81c1d93c61ccf3df60a65e3a5a1e465e044059d36b6fc1202b940c46c4c1e1"},
{file = "google-cloud-aiplatform-1.30.1.tar.gz", hash = "sha256:7552a6b2e66d7a9ff3c4b2bb95b0e9c182e7475dfb35d6347e9299f78779135a"},
{file = "google_cloud_aiplatform-1.30.1-py2.py3-none-any.whl", hash = "sha256:ab1bbd4cf83cf583b7dea7e53421ad076f18b63e93cb22fb53c03176d5aa9258"},
]
[package.dependencies]
@ -2953,17 +2979,17 @@ test = ["psutil", "pytest", "pytest-asyncio"]
[[package]]
name = "langsmith"
version = "0.0.20"
version = "0.0.22"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "langsmith-0.0.20-py3-none-any.whl", hash = "sha256:c393a45f6da79c7dfbeb778eebce660020d1c26ec9579fe997c8455a6765e4b4"},
{file = "langsmith-0.0.20.tar.gz", hash = "sha256:825056a6ee4583e3dd473e7f47ef45e85841517d84162fbb164771aae4aa391d"},
{file = "langsmith-0.0.22-py3-none-any.whl", hash = "sha256:1bc94a2e5bfa355ca15d9e658c2c2d04c8cc45c61892a1be08a7c3b40f2fd3f4"},
{file = "langsmith-0.0.22.tar.gz", hash = "sha256:5726c7841294db2a9e5863e20718878d16e28722bdaf3169a278ff3bda2f0be7"},
]
[package.dependencies]
pydantic = ">=1,<2"
pydantic = ">=1,<3"
requests = ">=2,<3"
[[package]]
@ -3032,10 +3058,13 @@ files = [
{file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"},
{file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"},
{file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"},
{file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"},
{file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"},
{file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"},
{file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"},
{file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"},
{file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"},
@ -3044,6 +3073,7 @@ files = [
{file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"},
{file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"},
{file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"},
@ -3063,6 +3093,7 @@ files = [
{file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"},
@ -3072,6 +3103,7 @@ files = [
{file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"},
@ -3081,6 +3113,7 @@ files = [
{file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"},
@ -3090,6 +3123,7 @@ files = [
{file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"},
{file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"},
@ -3100,13 +3134,16 @@ files = [
{file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"},
{file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"},
{file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"},
{file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"},
{file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"},
{file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"},
@ -3355,13 +3392,13 @@ files = [
[[package]]
name = "metaphor-python"
version = "0.1.11"
version = "0.1.14"
description = "A Python package for the Metaphor API."
optional = false
python-versions = "*"
files = [
{file = "metaphor-python-0.1.11.tar.gz", hash = "sha256:80fd993c44cc9d453d99eb65b95147d305f542fdd6fda699e3852e3100beb6ec"},
{file = "metaphor_python-0.1.11-py3-none-any.whl", hash = "sha256:0d759ecdf73492a4bafd404d0444935c172bcc4a89334f4f6780863ba488b238"},
{file = "metaphor-python-0.1.14.tar.gz", hash = "sha256:9c9be35d1270e1e2984637711c15b614ad852fbd2b51d428b4cc478e95018fab"},
{file = "metaphor_python-0.1.14-py3-none-any.whl", hash = "sha256:9435fb702d62032e4affbf7825fe4b1f0c6099d33b4867f4238c07f4515eb9a2"},
]
[package.dependencies]
@ -4236,17 +4273,17 @@ xml = ["lxml (>=4.6.3)"]
[[package]]
name = "pandas-stubs"
version = "2.0.2.230605"
version = "2.0.3.230814"
description = "Type annotations for pandas"
optional = false
python-versions = ">=3.8"
files = [
{file = "pandas_stubs-2.0.2.230605-py3-none-any.whl", hash = "sha256:39106b602f3cb6dc5f728b84e1b32bde6ecf41ee34ee714c66228009609fbada"},
{file = "pandas_stubs-2.0.2.230605.tar.gz", hash = "sha256:624c7bb06d38145a44b61be459ccd19b038e0bf20364a025ecaab78fea65e858"},
{file = "pandas_stubs-2.0.3.230814-py3-none-any.whl", hash = "sha256:4b3dfc027d49779176b7daa031a3405f7b839bcb6e312f4b9f29fea5feec5b4f"},
{file = "pandas_stubs-2.0.3.230814.tar.gz", hash = "sha256:1d5cc09e36e3d9f9a1ed9dceae4e03eeb26d1b898dd769996925f784365c8769"},
]
[package.dependencies]
numpy = ">=1.24.3"
numpy = {version = ">=1.25.0", markers = "python_version >= \"3.9\""}
types-pytz = ">=2022.1.1"
[[package]]
@ -4264,6 +4301,23 @@ files = [
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["docopt", "pytest (<6.0.0)"]
[[package]]
name = "passlib"
version = "1.7.4"
description = "comprehensive password hashing framework supporting over 30 schemes"
optional = false
python-versions = "*"
files = [
{file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"},
{file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"},
]
[package.extras]
argon2 = ["argon2-cffi (>=18.2.0)"]
bcrypt = ["bcrypt (>=3.1.0)"]
build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"]
totp = ["cryptography"]
[[package]]
name = "pathspec"
version = "0.11.2"
@ -4359,6 +4413,7 @@ files = [
{file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"},
{file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"},
{file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"},
{file = "Pillow-10.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37"},
{file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"},
{file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"},
{file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"},
@ -4368,6 +4423,7 @@ files = [
{file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"},
{file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"},
{file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"},
{file = "Pillow-10.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca"},
{file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"},
{file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"},
{file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"},
@ -5129,13 +5185,13 @@ diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pypdf"
version = "3.15.0"
version = "3.15.1"
description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
optional = false
python-versions = ">=3.6"
files = [
{file = "pypdf-3.15.0-py3-none-any.whl", hash = "sha256:2e29ddb62561ec91157c784783714703ddd3ce08f070ecbc57404fb86cd9fc97"},
{file = "pypdf-3.15.0.tar.gz", hash = "sha256:8a6264e1c47c63dc2484e29bdfa76b121435896a84e94b7c5ae82c6ae96354bb"},
{file = "pypdf-3.15.1-py3-none-any.whl", hash = "sha256:99b337af7da8046d1e2e94354846e8c56753e1cdc817ac0fbe770c1e2281902b"},
{file = "pypdf-3.15.1.tar.gz", hash = "sha256:d0dfaf4f10dfb06ac39e1d6a9cbffd63e77621d1e89c0ef08f346fd902df7b4b"},
]
[package.dependencies]
@ -6444,13 +6500,13 @@ widechars = ["wcwidth"]
[[package]]
name = "tenacity"
version = "8.2.2"
version = "8.2.3"
description = "Retry code until it succeeds"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
files = [
{file = "tenacity-8.2.2-py3-none-any.whl", hash = "sha256:2f277afb21b851637e8f52e6a613ff08734c347dc19ade928e519d7d2d8569b0"},
{file = "tenacity-8.2.2.tar.gz", hash = "sha256:43af037822bd0029025877f3b2d97cc4d7bb0c2991000a3d59d71517c5c969e0"},
{file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
{file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
]
[package.extras]
@ -6695,22 +6751,22 @@ scipy = ["scipy"]
[[package]]
name = "tornado"
version = "6.3.2"
version = "6.3.3"
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
optional = false
python-versions = ">= 3.8"
files = [
{file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"},
{file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"},
{file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"},
{file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"},
{file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"},
{file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"},
{file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"},
{file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"},
{file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"},
{file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"},
{file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"},
{file = "tornado-6.3.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d"},
{file = "tornado-6.3.3-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a"},
{file = "tornado-6.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f"},
{file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a"},
{file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2"},
{file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0"},
{file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16"},
{file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17"},
{file = "tornado-6.3.3-cp38-abi3-win32.whl", hash = "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3"},
{file = "tornado-6.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5"},
{file = "tornado-6.3.3.tar.gz", hash = "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe"},
]
[[package]]
@ -6896,13 +6952,13 @@ files = [
[[package]]
name = "types-pytz"
version = "2023.3.0.0"
version = "2023.3.0.1"
description = "Typing stubs for pytz"
optional = false
python-versions = "*"
files = [
{file = "types-pytz-2023.3.0.0.tar.gz", hash = "sha256:ecdc70d543aaf3616a7e48631543a884f74205f284cefd6649ddf44c6a820aac"},
{file = "types_pytz-2023.3.0.0-py3-none-any.whl", hash = "sha256:4fc2a7fbbc315f0b6630e0b899fd6c743705abe1094d007b0e612d10da15e0f3"},
{file = "types-pytz-2023.3.0.1.tar.gz", hash = "sha256:1a7b8d4aac70981cfa24478a41eadfcd96a087c986d6f150d77e3ceb3c2bdfab"},
{file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"},
]
[[package]]
@ -7137,20 +7193,15 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my
[[package]]
name = "validators"
version = "0.20.0"
description = "Python Data Validation for Humans™."
version = "0.21.0"
description = "Python Data Validation for Humans™"
optional = false
python-versions = ">=3.4"
python-versions = ">=3.8,<4.0"
files = [
{file = "validators-0.20.0.tar.gz", hash = "sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a"},
{file = "validators-0.21.0-py3-none-any.whl", hash = "sha256:3470db6f2384c49727ee319afa2e97aec3f8fad736faa6067e0fd7f9eaf2c551"},
{file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"},
]
[package.dependencies]
decorator = ">=3.4.0"
[package.extras]
test = ["flake8 (>=2.4.0)", "isort (>=4.2.2)", "pytest (>=2.2.3)"]
[[package]]
name = "watchfiles"
version = "0.19.0"
@ -7642,4 +7693,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.11"
content-hash = "9ac584c1cb5292e437fb4026f507a1ee342f7fd19248ef76f7765ec1bcab3211"
content-hash = "0f7d3f932eb130e7f3b6d234fc36e403ef409d63cfc825ba9f4c470203425ad2"

View file

@ -78,6 +78,9 @@ psycopg-binary = "^3.1.9"
fastavro = "^1.8.0"
langchain-experimental = "^0.0.8"
alembic = "^1.11.2"
passlib = "^1.7.4"
bcrypt = "^4.0.1"
python-jose = "^3.3.0"
metaphor-python = "^0.1.11"
[tool.poetry.group.dev.dependencies]

View file

View file

@ -0,0 +1,177 @@
from uuid import UUID
from typing import Annotated
from jose import JWTError, jwt
from sqlalchemy.orm import Session
from passlib.context import CryptContext
from fastapi.security import OAuth2PasswordBearer
from fastapi import Depends, HTTPException, status
from datetime import datetime, timedelta, timezone
from langflow.services.utils import get_settings_manager
from langflow.services.utils import get_session
from langflow.database.models.user import (
User,
get_user_by_id,
get_user_by_username,
update_user_last_login_at,
)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)], db: Session = Depends(get_session)
) -> User:
settings_manager = get_settings_manager()
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(
token,
settings_manager.settings.SECRET_KEY,
algorithms=[settings_manager.settings.ALGORITHM],
)
user_id: UUID = payload.get("sub") # type: ignore
token_type: str = payload.get("type") # type: ignore
if user_id is None or token_type:
raise credentials_exception
except JWTError as e:
raise credentials_exception from e
user = get_user_by_id(db, user_id) # type: ignore
if user is None:
raise credentials_exception
return user
async def get_current_active_user(
current_user: Annotated[User, Depends(get_current_user)]
):
if not current_user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_token(data: dict, expires_delta: timedelta):
settings_manager = get_settings_manager()
to_encode = data.copy()
expire = datetime.now(timezone.utc) + expires_delta
to_encode["exp"] = expire
return jwt.encode(
to_encode,
settings_manager.settings.SECRET_KEY,
algorithm=settings_manager.settings.ALGORITHM,
)
def create_user_longterm_token(
user_id: UUID, db: Session = Depends(get_session), update_last_login: bool = False
) -> dict:
access_token_expires_longterm = timedelta(days=365)
access_token = create_token(
data={"sub": str(user_id)},
expires_delta=access_token_expires_longterm,
)
# Update: last_login_at
if update_last_login:
update_user_last_login_at(user_id, db)
return {
"access_token": access_token,
"refresh_token": None,
"token_type": "bearer",
}
def create_user_tokens(
user_id: UUID, db: Session = Depends(get_session), update_last_login: bool = False
) -> dict:
settings_manager = get_settings_manager()
access_token_expires = timedelta(
minutes=settings_manager.settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
access_token = create_token(
data={"sub": str(user_id)},
expires_delta=access_token_expires,
)
refresh_token_expires = timedelta(
minutes=settings_manager.settings.REFRESH_TOKEN_EXPIRE_MINUTES
)
refresh_token = create_token(
data={"sub": str(user_id), "type": "rf"},
expires_delta=refresh_token_expires,
)
# Update: last_login_at
if update_last_login:
update_user_last_login_at(user_id, db)
return {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer",
}
def create_refresh_token(refresh_token: str, db: Session = Depends(get_session)):
settings_manager = get_settings_manager()
try:
payload = jwt.decode(
refresh_token,
settings_manager.settings.SECRET_KEY,
algorithms=[settings_manager.settings.ALGORITHM],
)
user_id: UUID = payload.get("sub") # type: ignore
token_type: str = payload.get("type") # type: ignore
if user_id is None or token_type is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token"
)
return create_user_tokens(user_id, db)
except JWTError as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token",
) from e
def authenticate_user(
username: str, password: str, db: Session = Depends(get_session)
) -> User | None:
user = get_user_by_username(db, username)
if not user:
return None
if not user.is_active:
if not user.last_login_at:
raise HTTPException(status_code=400, detail="Waiting for approval")
raise HTTPException(status_code=400, detail="Inactive user")
return user if verify_password(password, user.password) else None

View file

@ -0,0 +1,7 @@
from pydantic import BaseModel
class Token(BaseModel):
access_token: str
refresh_token: str
token_type: str

View file

@ -0,0 +1,94 @@
from sqlmodel import Field
from uuid import UUID, uuid4
from pydantic import BaseModel
from typing import Optional, List
from sqlalchemy.orm import Session
from datetime import timezone, datetime
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, Depends
from langflow.services.utils import get_session
from langflow.services.database.models.base import SQLModelSerializable, SQLModel
class User(SQLModelSerializable, table=True):
id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True)
username: str = Field(index=True, unique=True)
password: str = Field()
is_active: bool = Field(default=False)
is_superuser: bool = Field(default=False)
create_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
last_login_at: Optional[datetime] = Field()
class UserAddModel(SQLModel):
username: str = Field()
password: str = Field()
class UserListModel(SQLModel):
id: UUID = Field(default_factory=uuid4)
username: str = Field()
is_active: bool = Field()
is_superuser: bool = Field()
create_at: datetime = Field()
updated_at: datetime = Field()
last_login_at: Optional[datetime] = Field()
class UserPatchModel(SQLModel):
username: Optional[str] = Field()
is_active: Optional[bool] = Field()
is_superuser: Optional[bool] = Field()
last_login_at: Optional[datetime] = Field()
class UsersResponse(BaseModel):
total_count: int
users: List[UserListModel]
def get_user_by_username(db: Session, username: str) -> User:
db_user = db.query(User).filter(User.username == username).first()
return User.from_orm(db_user) if db_user else None # type: ignore
def get_user_by_id(db: Session, id: UUID) -> User:
db_user = db.query(User).filter(User.id == id).first()
return User.from_orm(db_user) if db_user else None # type: ignore
def update_user(
user_id: UUID, user: UserPatchModel, db: Session = Depends(get_session)
) -> User:
user_db = get_user_by_username(db, user.username) # type: ignore
if user_db and user_db.id != user_id:
raise HTTPException(status_code=409, detail="Username already exists")
user_db = get_user_by_id(db, user_id)
if not user_db:
raise HTTPException(status_code=404, detail="User not found")
try:
user_data = user.dict(exclude_unset=True)
for key, value in user_data.items():
setattr(user_db, key, value)
user_db.updated_at = datetime.now(timezone.utc)
user_db = db.merge(user_db)
db.commit()
if db.identity_key(instance=user_db) is not None:
db.refresh(user_db)
except IntegrityError as e:
db.rollback()
raise HTTPException(status_code=400, detail=str(e)) from e
return user_db
def update_user_last_login_at(user_id: UUID, db: Session = Depends(get_session)):
user_data = UserPatchModel(last_login_at=datetime.now(timezone.utc)) # type: ignore
return update_user(user_id, user_data, db)

View file

@ -6,6 +6,8 @@ from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from langflow.api import router
from langflow.routers import login, users, health
from langflow.interface.utils import setup_llm_caching
from langflow.services.database.utils import initialize_database
from langflow.services.manager import initialize_services
@ -19,13 +21,7 @@ def create_app():
app = FastAPI()
origins = [
"*",
]
@app.get("/health")
def get_health():
return {"status": "OK"}
origins = ["*"]
app.add_middleware(
CORSMiddleware,
@ -34,6 +30,11 @@ def create_app():
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(login.router)
app.include_router(users.router)
app.include_router(health.router)
app.include_router(router)
app.on_event("startup")(initialize_services)

View file

View file

@ -0,0 +1,8 @@
from fastapi import APIRouter
router = APIRouter()
@router.get("/health")
def get_health():
return {"status": "OK"}

View file

@ -0,0 +1,62 @@
from uuid import UUID
from sqlalchemy.orm import Session
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from langflow.services.utils import get_session
from langflow.database.models.token import Token
from langflow.auth.auth import (
authenticate_user,
create_user_tokens,
create_refresh_token,
create_user_longterm_token,
)
from langflow.services.utils import get_settings_manager
router = APIRouter()
@router.post("/login", response_model=Token)
async def login_to_get_access_token(
form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_session),
# _: Session = Depends(get_current_active_user)
):
if user := authenticate_user(form_data.username, form_data.password, db):
return create_user_tokens(user_id=user.id, db=db, update_last_login=True)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
@router.get("/auto_login")
async def auto_login(db: Session = Depends(get_session)):
settings_manager = get_settings_manager()
if settings_manager.settings.AUTO_LOGIN:
user_id = UUID("3fa85f64-5717-4562-b3fc-2c963f66afa6")
return create_user_longterm_token(user_id, db)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={
"message": "Auto login is disabled. Please enable it in the settings",
"auto_login": False,
},
)
@router.post("/refresh")
async def refresh_token(token: str):
if token:
return create_refresh_token(token)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token",
headers={"WWW-Authenticate": "Bearer"},
)

View file

@ -0,0 +1,133 @@
from uuid import UUID
from sqlalchemy import func
from sqlalchemy.exc import IntegrityError
from sqlmodel import Session, select
from fastapi import APIRouter, Depends, HTTPException
from langflow.services.utils import get_session
from langflow.auth.auth import get_current_active_user, get_password_hash
from langflow.database.models.user import (
User,
UserAddModel,
UserListModel,
UserPatchModel,
UsersResponse,
update_user,
)
router = APIRouter(tags=["Login"])
@router.post("/user", response_model=UserListModel)
def add_user(
user: UserAddModel,
db: Session = Depends(get_session),
) -> User:
"""
Add a new user to the database.
"""
new_user = User(**user.dict())
try:
new_user.password = get_password_hash(user.password)
db.add(new_user)
db.commit()
db.refresh(new_user)
except IntegrityError as e:
db.rollback()
raise HTTPException(status_code=400, detail="User exists") from e
return new_user
@router.get("/user", response_model=UserListModel)
def read_current_user(current_user: User = Depends(get_current_active_user)) -> User:
"""
Retrieve the current user's data.
"""
return current_user
@router.get("/users", response_model=UsersResponse)
def read_all_users(
skip: int = 0,
limit: int = 10,
_: Session = Depends(get_current_active_user),
db: Session = Depends(get_session),
) -> UsersResponse:
"""
Retrieve a list of users from the database with pagination.
"""
query = select(User).offset(skip).limit(limit)
users = db.execute(query).fetchall()
count_query = select(func.count()).select_from(User) # type: ignore
total_count = db.execute(count_query).scalar()
return UsersResponse(
total_count=total_count, # type: ignore
users=[UserListModel(**dict(user.User)) for user in users],
)
@router.patch("/user/{user_id}", response_model=UserListModel)
def patch_user(
user_id: UUID,
user: UserPatchModel,
_: Session = Depends(get_current_active_user),
db: Session = Depends(get_session),
) -> User:
"""
Update an existing user's data.
"""
return update_user(user_id, user, db)
@router.delete("/user/{user_id}")
def delete_user(
user_id: UUID,
_: Session = Depends(get_current_active_user),
db: Session = Depends(get_session),
) -> dict:
"""
Delete a user from the database.
"""
user_db = db.query(User).filter(User.id == user_id).first()
if not user_db:
raise HTTPException(status_code=404, detail="User not found")
db.delete(user_db)
db.commit()
return {"detail": "User deleted"}
# TODO: REMOVE - Just for testing purposes
@router.post("/super_user", response_model=User)
def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
db: Session = Depends(get_session),
) -> User:
"""
Add a superuser for testing purposes.
(This should be removed in production)
"""
new_user = User(
username="superuser",
password="12345",
is_active=True,
is_superuser=True,
last_login_at=None,
)
try:
new_user.password = get_password_hash(new_user.password)
db.add(new_user)
db.commit()
db.refresh(new_user)
except IntegrityError as e:
db.rollback()
raise HTTPException(status_code=400, detail="User exists") from e
return new_user

View file

@ -1,11 +1,16 @@
from pathlib import Path
from typing import TYPE_CHECKING
from langflow.services.base import Service
from langflow.services.utils import get_settings_manager
from sqlmodel import SQLModel, Session, create_engine
from langflow.utils.logger import logger
from alembic.config import Config
from alembic import command
from langflow.services.database import models # noqa
if TYPE_CHECKING:
from sqlalchemy.engine import Engine
class DatabaseManager(Service):
name = "database_manager"
@ -17,7 +22,19 @@ class DatabaseManager(Service):
langflow_dir = Path(__file__).parent.parent.parent
self.script_location = langflow_dir / "alembic"
self.alembic_cfg_path = langflow_dir / "alembic.ini"
self.engine = create_engine(database_url)
self.engine = self._create_engine()
def _create_engine(self) -> "Engine":
"""Create the engine for the database."""
settings_manager = get_settings_manager()
if (
settings_manager.settings.DATABASE_URL
and settings_manager.settings.DATABASE_URL.startswith("sqlite")
):
connect_args = {"check_same_thread": False}
else:
connect_args = {}
return create_engine(self.database_url, connect_args=connect_args)
def __enter__(self):
self._session = Session(self.engine)

View file

@ -2,6 +2,7 @@ import contextlib
import json
import os
from shutil import copy2
import secrets
from typing import Optional, List
from pathlib import Path
@ -39,6 +40,15 @@ class Settings(BaseSettings):
REMOVE_API_KEYS: bool = False
COMPONENTS_PATH: List[str] = []
# Login settings
SECRET_KEY: str = secrets.token_hex(32)
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
REFRESH_TOKEN_EXPIRE_MINUTES: int = 70
# If AUTO_LOGIN = True
# > The application does not request login and logs in automatically as a super user.
AUTO_LOGIN: bool = True
@validator("CONFIG_DIR", pre=True, allow_reuse=True)
def set_langflow_dir(cls, value):
if not value:

View file

@ -1,171 +0,0 @@
import contextlib
import json
import os
from typing import Optional, List
from pathlib import Path
import yaml
from pydantic import BaseSettings, root_validator, validator
from langflow.utils.logger import logger
BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
class Settings(BaseSettings):
CHAINS: dict = {}
AGENTS: dict = {}
PROMPTS: dict = {}
LLMS: dict = {}
TOOLS: dict = {}
MEMORIES: dict = {}
EMBEDDINGS: dict = {}
VECTORSTORES: dict = {}
DOCUMENTLOADERS: dict = {}
WRAPPERS: dict = {}
RETRIEVERS: dict = {}
TOOLKITS: dict = {}
TEXTSPLITTERS: dict = {}
UTILITIES: dict = {}
OUTPUT_PARSERS: dict = {}
CUSTOM_COMPONENTS: dict = {}
DEV: bool = False
DATABASE_URL: Optional[str] = None
CACHE: str = "InMemoryCache"
REMOVE_API_KEYS: bool = False
COMPONENTS_PATH: List[str] = []
@validator("DATABASE_URL", pre=True)
def set_database_url(cls, value):
if not value:
logger.debug(
"No database_url provided, trying LANGFLOW_DATABASE_URL env variable"
)
if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"):
value = langflow_database_url
logger.debug("Using LANGFLOW_DATABASE_URL env variable.")
else:
logger.debug("No DATABASE_URL env variable, using sqlite database")
value = "sqlite:///./langflow.db"
return value
@validator("COMPONENTS_PATH", pre=True)
def set_components_path(cls, value):
if os.getenv("LANGFLOW_COMPONENTS_PATH"):
logger.debug("Adding LANGFLOW_COMPONENTS_PATH to components_path")
langflow_component_path = os.getenv("LANGFLOW_COMPONENTS_PATH")
if (
Path(langflow_component_path).exists()
and langflow_component_path not in value
):
if isinstance(langflow_component_path, list):
for path in langflow_component_path:
if path not in value:
value.append(path)
logger.debug(
f"Extending {langflow_component_path} to components_path"
)
elif langflow_component_path not in value:
value.append(langflow_component_path)
logger.debug(
f"Appending {langflow_component_path} to components_path"
)
if not value:
value = [BASE_COMPONENTS_PATH]
logger.debug("Setting default components path to components_path")
elif BASE_COMPONENTS_PATH not in value:
value.append(BASE_COMPONENTS_PATH)
logger.debug("Adding default components path to components_path")
logger.debug(f"Components path: {value}")
return value
class Config:
validate_assignment = True
extra = "ignore"
env_prefix = "LANGFLOW_"
@root_validator(allow_reuse=True)
def validate_lists(cls, values):
for key, value in values.items():
if key != "dev" and not value:
values[key] = []
return values
def update_from_yaml(self, file_path: str, dev: bool = False):
new_settings = load_settings_from_yaml(file_path)
self.CHAINS = new_settings.CHAINS or {}
self.AGENTS = new_settings.AGENTS or {}
self.PROMPTS = new_settings.PROMPTS or {}
self.LLMS = new_settings.LLMS or {}
self.TOOLS = new_settings.TOOLS or {}
self.MEMORIES = new_settings.MEMORIES or {}
self.WRAPPERS = new_settings.WRAPPERS or {}
self.TOOLKITS = new_settings.TOOLKITS or {}
self.TEXTSPLITTERS = new_settings.TEXTSPLITTERS or {}
self.UTILITIES = new_settings.UTILITIES or {}
self.EMBEDDINGS = new_settings.EMBEDDINGS or {}
self.VECTORSTORES = new_settings.VECTORSTORES or {}
self.DOCUMENTLOADERS = new_settings.DOCUMENTLOADERS or {}
self.RETRIEVERS = new_settings.RETRIEVERS or {}
self.OUTPUT_PARSERS = new_settings.OUTPUT_PARSERS or {}
self.CUSTOM_COMPONENTS = new_settings.CUSTOM_COMPONENTS or {}
self.COMPONENTS_PATH = new_settings.COMPONENTS_PATH or []
self.DEV = dev
def update_settings(self, **kwargs):
logger.debug("Updating settings")
for key, value in kwargs.items():
# value may contain sensitive information, so we don't want to log it
if not hasattr(self, key):
logger.debug(f"Key {key} not found in settings")
continue
logger.debug(f"Updating {key}")
if isinstance(getattr(self, key), list):
# value might be a '[something]' string
with contextlib.suppress(json.decoder.JSONDecodeError):
value = json.loads(str(value))
if isinstance(value, list):
for item in value:
if item not in getattr(self, key):
getattr(self, key).append(item)
logger.debug(f"Extended {key}")
else:
getattr(self, key).append(value)
logger.debug(f"Appended {key}")
else:
setattr(self, key, value)
logger.debug(f"Updated {key}")
logger.debug(f"{key}: {getattr(self, key)}")
def save_settings_to_yaml(settings: Settings, file_path: str):
with open(file_path, "w") as f:
settings_dict = settings.dict()
yaml.dump(settings_dict, f)
def load_settings_from_yaml(file_path: str) -> Settings:
# Check if a string is a valid path or a file name
if "/" not in file_path:
# Get current path
current_path = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(current_path, file_path)
with open(file_path, "r") as f:
settings_dict = yaml.safe_load(f)
settings_dict = {k.upper(): v for k, v in settings_dict.items()}
for key in settings_dict:
if key not in Settings.__fields__.keys():
raise KeyError(f"Key {key} not found in settings")
logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
return Settings(**settings_dict)
settings = load_settings_from_yaml("config.yaml")

File diff suppressed because it is too large Load diff

View file

@ -12,11 +12,13 @@
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-form": "^0.0.3",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-menubar": "^1.0.3",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^1.2.2",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
@ -69,7 +71,8 @@
"start": "vite",
"build": "vite build",
"serve": "vite preview",
"format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\""
"format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
},
"eslintConfig": {
"extends": [

View file

@ -46,6 +46,10 @@ export default function App() {
}>
>([]);
const isLoginPage = location.pathname.includes("login");
const isAdminPage = location.pathname.includes("admin");
const isSignUpPage = location.pathname.includes("signup");
// Use effect hook to update alertsList when a new alert is added
useEffect(() => {
// If there is an error alert open with data, add it to the alertsList
@ -133,7 +137,7 @@ export default function App() {
}}
FallbackComponent={CrashErrorComponent}
>
<Header />
{!isLoginPage && !isSignUpPage && <Header />}
<Router />
</ErrorBoundary>
<div></div>

View file

@ -111,6 +111,7 @@ export default function ParameterComponent({
let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
if (groupedObj && groupedObj.length > 0) {
//@ts-ignore
//@ts-ignore
refHtml.current = groupedObj.map((item, index) => {
const Icon: any =

View file

@ -8,6 +8,7 @@ import { useSSE } from "../../contexts/SSEContext";
import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { validationStatusType } from "../../types/components";
import { NodeDataType } from "../../types/flow";
import { cleanEdges } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
@ -20,13 +21,14 @@ export default function GenericNode({
}: {
data: NodeDataType;
selected: boolean;
}) {
}): JSX.Element {
const [data, setData] = useState(olddata);
const { updateFlow, flows, tabId } = useContext(TabsContext);
const updateNodeInternals = useUpdateNodeInternals();
const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
const [validationStatus, setValidationStatus] = useState(null);
const [validationStatus, setValidationStatus] =
useState<validationStatusType | null>(null);
// State for outline color
const { sseData, isBuilding } = useSSE();
useEffect(() => {
@ -41,7 +43,7 @@ export default function GenericNode({
nodes: flow.data.nodes,
},
updateEdge: (edge) => {
flow.data.edges = edge;
flow.data!.edges = edge;
reactFlowInstance.setEdges(edge);
updateNodeInternals(data.id);
},
@ -77,7 +79,7 @@ export default function GenericNode({
"generic-node-div"
)}
>
{data.node.beta && (
{data.node?.beta && (
<div className="beta-badge-wrapper">
<div className="beta-badge-content">BETA</div>
</div>
@ -90,9 +92,9 @@ export default function GenericNode({
iconColor={`${nodeColors[types[data.type]]}`}
/>
<div className="generic-node-tooltip-div">
<ShadTooltip content={data.node.display_name}>
<ShadTooltip content={data.node?.display_name}>
<div className="generic-node-tooltip-div text-primary">
{data.node.display_name}
{data.node?.display_name}
</div>
</ShadTooltip>
</div>
@ -117,7 +119,9 @@ export default function GenericNode({
{typeof validationStatus.params === "string"
? validationStatus.params
.split("\n")
.map((line, index) => <div key={index}>{line}</div>)
.map((line: string, index: number) => (
<div key={index}>{line}</div>
))
: ""}
</div>
)
@ -155,20 +159,20 @@ export default function GenericNode({
</div>
<div className="generic-node-desc">
<div className="generic-node-desc-text">{data.node.description}</div>
<div className="generic-node-desc-text">{data.node?.description}</div>
<>
{Object.keys(data.node.template)
{Object.keys(data.node!.template)
.filter((templateField) => templateField.charAt(0) !== "_")
.map((templateField: string, idx) => (
<div key={idx}>
{data.node.template[templateField].show &&
!data.node.template[templateField].advanced ? (
{data.node!.template[templateField].show &&
!data.node!.template[templateField].advanced ? (
<ParameterComponent
key={
(data.node.template[templateField].input_types?.join(
(data.node!.template[templateField].input_types?.join(
";"
) ?? data.node.template[templateField].type) +
) ?? data.node!.template[templateField].type) +
"|" +
templateField +
"|" +
@ -178,39 +182,39 @@ export default function GenericNode({
setData={setData}
color={
nodeColors[
types[data.node.template[templateField].type]
types[data.node?.template[templateField].type!]
] ??
nodeColors[data.node.template[templateField].type] ??
nodeColors[data.node?.template[templateField].type!] ??
nodeColors.unknown
}
title={
data.node.template[templateField].display_name
data.node?.template[templateField].display_name
? data.node.template[templateField].display_name
: data.node.template[templateField].name
: data.node?.template[templateField].name
? toTitleCase(data.node.template[templateField].name)
: toTitleCase(templateField)
}
info={data.node.template[templateField].info}
info={data.node?.template[templateField].info}
name={templateField}
tooltipTitle={
data.node.template[templateField].input_types?.join(
data.node?.template[templateField].input_types?.join(
"\n"
) ?? data.node.template[templateField].type
) ?? data.node?.template[templateField].type
}
required={data.node.template[templateField].required}
required={data.node?.template[templateField].required}
id={
(data.node.template[templateField].input_types?.join(
(data.node?.template[templateField].input_types?.join(
";"
) ?? data.node.template[templateField].type) +
) ?? data.node?.template[templateField].type) +
"|" +
templateField +
"|" +
data.id
}
left={true}
type={data.node.template[templateField].type}
type={data.node?.template[templateField].type}
optionalHandle={
data.node.template[templateField].input_types
data.node?.template[templateField].input_types
}
/>
) : (
@ -220,25 +224,25 @@ export default function GenericNode({
))}
<div
className={classNames(
Object.keys(data.node.template).length < 1 ? "hidden" : "",
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
"flex-max-width justify-center"
)}
>
{" "}
</div>
<ParameterComponent
key={[data.type, data.id, ...data.node.base_classes].join("|")}
key={[data.type, data.id, ...data.node!.base_classes].join("|")}
data={data}
setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={
data.node.output_types && data.node.output_types.length > 0
data.node?.output_types && data.node.output_types.length > 0
? data.node.output_types.join("|")
: data.type
}
tooltipTitle={data.node.base_classes.join("\n")}
id={[data.type, data.id, ...data.node.base_classes].join("|")}
type={data.node.base_classes.join("|")}
tooltipTitle={data.node?.base_classes.join("\n")}
id={[data.type, data.id, ...data.node!.base_classes].join("|")}
type={data.node?.base_classes.join("|")}
left={false}
/>
</>

View file

@ -7,7 +7,7 @@ import { SingleAlertComponentType } from "../../../../types/alerts";
export default function SingleAlert({
dropItem,
removeAlert,
}: SingleAlertComponentType) {
}: SingleAlertComponentType): JSX.Element {
const [show, setShow] = useState(true);
const type = dropItem.type;

View file

@ -9,7 +9,9 @@ import { alertContext } from "../../contexts/alertContext";
import { AlertDropdownType } from "../../types/alerts";
import SingleAlert from "./components/singleAlertComponent";
export default function AlertDropdown({ children }: AlertDropdownType) {
export default function AlertDropdown({
children,
}: AlertDropdownType): JSX.Element {
const {
notificationList,
clearNotificationList,

View file

@ -8,7 +8,7 @@ export default function ErrorAlert({
list = [],
id,
removeAlert,
}: ErrorAlertType) {
}: ErrorAlertType): JSX.Element {
const [show, setShow] = useState(true);
useEffect(() => {
if (show) {

View file

@ -2,7 +2,7 @@ import { useEffect } from "react";
export function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = (event) => {
const listener = (event: Event) => {
// Do nothing if clicking ref's element or its children
if (!ref.current || ref.current.contains(event.target)) {
return;

View file

@ -9,7 +9,7 @@ export default function NoticeAlert({
link = "",
id,
removeAlert,
}: NoticeAlertType) {
}: NoticeAlertType): JSX.Element {
const [show, setShow] = useState(true);
useEffect(() => {
if (show) {

View file

@ -7,7 +7,7 @@ export default function SuccessAlert({
title,
id,
removeAlert,
}: SuccessAlertType) {
}: SuccessAlertType): JSX.Element {
const [show, setShow] = useState(true);
useEffect(() => {
if (show) {

View file

@ -12,12 +12,12 @@ export default function AccordionComponent({
children,
open = [],
keyValue,
}: AccordionComponentType) {
}: AccordionComponentType): JSX.Element {
const [value, setValue] = useState(
open.length === 0 ? "" : getOpenAccordion()
);
function getOpenAccordion() {
function getOpenAccordion(): string {
let value = "";
open.forEach((el) => {
if (el == trigger) {
@ -28,8 +28,8 @@ export default function AccordionComponent({
return value;
}
function handleClick() {
value === "" ? setValue(keyValue) : setValue("");
function handleClick(): void {
value === "" ? setValue(keyValue!) : setValue("");
}
return (
@ -40,7 +40,7 @@ export default function AccordionComponent({
value={value}
onValueChange={setValue}
>
<AccordionItem value={keyValue} className="border-b">
<AccordionItem value={keyValue!} className="border-b">
<AccordionTrigger
onClick={() => {
handleClick();

View file

@ -1,4 +1,9 @@
export default function CrashErrorComponent({ error, resetErrorBoundary }) {
import { crashComponentPropsType } from "../../types/components";
export default function CrashErrorComponent({
error,
resetErrorBoundary,
}: crashComponentPropsType): JSX.Element {
return (
<div className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-foreground bg-opacity-50">
<div className="flex h-1/3 min-h-fit max-w-4xl flex-col justify-evenly rounded-lg bg-background p-8 text-start shadow-lg">

View file

@ -3,19 +3,8 @@ import { Input } from "../../components/ui/input";
import { Label } from "../../components/ui/label";
import { Textarea } from "../../components/ui/textarea";
import { readFlowsFromDatabase } from "../../controllers/API";
type InputProps = {
name: string | null;
description: string | null;
maxLength?: number;
flows: Array<{ id: string; name: string; description: string }>;
tabId: string;
invalidName: boolean;
setInvalidName: (invalidName: boolean) => void;
setName: (name: string) => void;
setDescription: (description: string) => void;
updateFlow: (flow: { id: string; name: string }) => void;
};
import { InputProps } from "../../types/components";
import { FlowType } from "../../types/flow";
export const EditFlowSettings: React.FC<InputProps> = ({
name,
@ -27,13 +16,12 @@ export const EditFlowSettings: React.FC<InputProps> = ({
tabId,
setName,
setDescription,
updateFlow,
}) => {
}: InputProps): JSX.Element => {
const [isMaxLength, setIsMaxLength] = useState(false);
const nameLists = useRef([]);
const nameLists = useRef<string[]>([]);
useEffect(() => {
readFlowsFromDatabase().then((flows) => {
flows.forEach((flow) => {
flows.forEach((flow: FlowType) => {
nameLists.current.push(flow.name);
});
});
@ -47,20 +35,20 @@ export const EditFlowSettings: React.FC<InputProps> = ({
setIsMaxLength(false);
}
if (!nameLists.current.includes(value)) {
setInvalidName(false);
setInvalidName!(false);
} else {
setInvalidName(true);
setInvalidName!(true);
}
setName(value);
};
const [desc, setDesc] = useState(
flows.find((flow) => flow.id === tabId).description
flows.find((flow) => flow.id === tabId)?.description
);
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
flows.find((flow) => flow.id === tabId).description = event.target.value;
setDesc(flows.find((flow) => flow.id === tabId).description);
flows.find((flow) => flow.id === tabId)!.description = event.target.value;
setDesc(flows.find((flow) => flow.id === tabId)?.description);
setDescription(event.target.value);
};

View file

@ -0,0 +1,118 @@
import { useState } from "react";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../../components/ui/select";
import { PaginatorComponentType } from "../../types/components";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
export default function PaginatorComponent({
pageSize = 10,
pageIndex = 1,
rowsCount = [10, 20, 30],
totalRowsCount = 0,
paginate,
}: PaginatorComponentType) {
const [size, setPageSize] = useState(pageSize);
const [index, setPageIndex] = useState(pageIndex);
const [maxIndex, setMaxPageIndex] = useState(
Math.ceil(totalRowsCount / pageSize)
);
return (
<>
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground"></div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Rows per page</p>
<Select
onValueChange={(pageSize: string) => {
setPageSize(Number(pageSize));
setMaxPageIndex(Math.ceil(totalRowsCount / Number(pageSize)));
paginate(Number(pageSize), index);
}}
>
<SelectTrigger className="w-[100px]">
<SelectValue placeholder="10" />
</SelectTrigger>
<SelectContent>
{rowsCount.map((item, i) => (
<SelectItem key={i} value={item.toString()}>
{item}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {index} of {maxIndex}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => {
setPageIndex(1);
paginate(size, 1);
}}
>
<span className="sr-only">Go to first page</span>
<IconComponent name="ChevronsLeft" className="h-4 w-4" />
</Button>
<Button
onClick={() => {
if (index <= 1) {
setPageIndex(1);
paginate(size, 1);
} else {
{
setPageIndex(index - 1);
paginate(size, index - 1);
}
}
}}
variant="outline"
className="h-8 w-8 p-0"
>
<span className="sr-only">Go to previous page</span>
<IconComponent name="ChevronLeft" className="h-4 w-4" />
</Button>
<Button
onClick={() => {
if (index >= maxIndex) {
setPageIndex(maxIndex);
paginate(size, maxIndex);
} else {
setPageIndex(index + 1);
paginate(size, index + 1);
}
}}
variant="outline"
className="h-8 w-8 p-0"
>
<span className="sr-only">Go to next page</span>
<IconComponent name="ChevronRight" className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => {
setPageIndex(maxIndex);
paginate(size, maxIndex);
}}
>
<span className="sr-only">Go to last page</span>
<IconComponent name="ChevronsRight" className="h-4 w-4" />
</Button>
</div>
</div>
</div>
</>
);
}

View file

@ -3,16 +3,16 @@ import { RadialProgressType } from "../../types/components";
export default function RadialProgressComponent({
value,
color,
}: RadialProgressType) {
}: RadialProgressType): JSX.Element {
const style = {
"--value": value * 100,
"--value": value! * 100,
"--size": "1.5rem",
"--thickness": "2px",
} as React.CSSProperties;
return (
<div className={"radial-progress " + color} style={style}>
<strong className="text-[8px]">{Math.trunc(value * 100)}%</strong>
<strong className="text-[8px]">{Math.trunc(value! * 100)}%</strong>
</div>
);
}

View file

@ -3,20 +3,9 @@ import type { FC } from "react";
import React from "react";
import { Tooltip as ReactTooltip } from "react-tooltip";
import "react-tooltip/dist/react-tooltip.css";
import { TooltipProps } from "../../types/components";
import { classNames } from "../../utils/utils";
type TooltipProps = {
selector: string;
content?: string;
disabled?: boolean;
htmlContent?: React.ReactNode;
className?: string; // This should use !impornant to override the default styles eg: '!bg-white'
position?: "top" | "right" | "bottom" | "left";
clickable?: boolean;
children: React.ReactNode;
delayShow?: number;
};
const TooltipReact: FC<TooltipProps> = ({
selector,
content,
@ -27,7 +16,7 @@ const TooltipReact: FC<TooltipProps> = ({
className,
clickable,
delayShow,
}) => {
}: TooltipProps): JSX.Element => {
return (
<div className="tooltip-container">
{React.cloneElement(children as React.ReactElement, {
@ -38,7 +27,7 @@ const TooltipReact: FC<TooltipProps> = ({
content={content}
className={classNames(
"z-[9999] !bg-white !text-xs !font-normal !text-foreground !opacity-100 !shadow-md",
className
className!
)}
place={position}
clickable={clickable}

View file

@ -1,11 +1,12 @@
import DOMPurify from "dompurify";
import { SanitizedHTMLWrapperType } from "../../types/components";
const SanitizedHTMLWrapper = ({
className,
content,
onClick,
suppressWarning = false,
}) => {
}: SanitizedHTMLWrapperType): JSX.Element => {
const sanitizedHTML = DOMPurify.sanitize(content);
return (

View file

@ -8,7 +8,7 @@ export default function ShadTooltip({
children,
styleClasses,
delayDuration = 500,
}: ShadToolTipType) {
}: ShadToolTipType): JSX.Element {
return (
<Tooltip delayDuration={delayDuration}>
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>

View file

@ -5,7 +5,7 @@ export default function Tooltip({
children,
title,
placement,
}: TooltipComponentType) {
}: TooltipComponentType): JSX.Element {
return (
<LightTooltip placement={placement} title={title} arrow>
{children}

View file

@ -1,6 +1,6 @@
import { useContext } from "react";
import { TabsContext } from "../../contexts/tabsContext";
import { FlowType } from "../../types/flow";
import { cardComponentPropsType } from "../../types/components";
import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent";
import {
@ -16,12 +16,7 @@ export const CardComponent = ({
id,
onDelete,
button,
}: {
flow: FlowType;
id: string;
onDelete?: () => void;
button?: JSX.Element;
}) => {
}: cardComponentPropsType): JSX.Element => {
const { removeFlow } = useContext(TabsContext);
return (

View file

@ -8,6 +8,8 @@ import { postBuildInit } from "../../../controllers/API";
import { FlowType } from "../../../types/flow";
import { TabsContext } from "../../../contexts/tabsContext";
import { parsedDataType } from "../../../types/components";
import { TabsState } from "../../../types/tabs";
import { validateNodes } from "../../../utils/reactflowUtils";
import RadialProgressComponent from "../../RadialProgress";
import IconComponent from "../../genericIconComponent";
@ -21,7 +23,7 @@ export default function BuildTrigger({
flow: FlowType;
setIsBuilt: any;
isBuilt: boolean;
}) {
}): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
const { reactFlowInstance } = useContext(typesContext);
const { setTabsState } = useContext(TabsContext);
@ -30,12 +32,12 @@ export default function BuildTrigger({
const eventClick = isBuilding ? "pointer-events-none" : "";
const [progress, setProgress] = useState(0);
async function handleBuild(flow: FlowType) {
async function handleBuild(flow: FlowType): Promise<void> {
try {
if (isBuilding) {
return;
}
const errors = validateNodes(reactFlowInstance);
const errors = validateNodes(reactFlowInstance!);
if (errors.length > 0) {
setErrorData({
title: "Oops! Looks like you missed something",
@ -69,7 +71,7 @@ export default function BuildTrigger({
const response = await postBuildInit(flow);
const { flowId } = response.data;
// Step 2: Use the session ID to establish an SSE connection using EventSource
let validationResults = [];
let validationResults: boolean[] = [];
let finished = false;
const apiUrl = `/api/v1/build/stream/${flowId}`;
const eventSource = new EventSource(apiUrl);
@ -91,7 +93,8 @@ export default function BuildTrigger({
// If the event is a log, log it
setSuccessData({ title: parsedData.log });
} else if (parsedData.input_keys !== undefined) {
setTabsState((old) => {
//@ts-ignore
setTabsState((old: TabsState) => {
return {
...old,
[flowId]: {
@ -120,13 +123,13 @@ export default function BuildTrigger({
// Step 3: Wait for the stream to finish
while (!finished) {
await new Promise((resolve) => setTimeout(resolve, 100));
finished = validationResults.length === flow.data.nodes.length;
finished = validationResults.length === flow.data!.nodes.length;
}
// Step 4: Return true if all nodes are valid, false otherwise
return validationResults.every((result) => result);
}
function processStreamResult(parsedData) {
function processStreamResult(parsedData: parsedDataType) {
// Process each chunk of data here
// Parse the chunk and update the context
try {

View file

@ -8,12 +8,18 @@ import {
FLOW_NOT_BUILT_TITLE,
} from "../../../constants/constants";
import { alertContext } from "../../../contexts/alertContext";
import { chatTriggerPropType } from "../../../types/components";
import IconComponent from "../../genericIconComponent";
export default function ChatTrigger({ open, setOpen, isBuilt, canOpen }) {
export default function ChatTrigger({
open,
setOpen,
isBuilt,
canOpen,
}: chatTriggerPropType): JSX.Element {
const { setErrorData } = useContext(alertContext);
function handleClick() {
function handleClick(): void {
if (isBuilt) {
if (canOpen) {
setOpen(true);

View file

@ -10,7 +10,7 @@ import { getBuildStatus } from "../../controllers/API";
import FormModal from "../../modals/formModal";
import { NodeType } from "../../types/flow";
export default function Chat({ flow }: ChatType) {
export default function Chat({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false);
const [canOpen, setCanOpen] = useState(false);
const { tabsState, isBuilt, setIsBuilt } = useContext(TabsContext);
@ -44,11 +44,11 @@ export default function Chat({ flow }: ChatType) {
}, [flow]);
const prevNodesRef = useRef<any[] | undefined>();
const nodes = useNodes();
const nodes: NodeType[] = useNodes();
useEffect(() => {
const prevNodes = prevNodesRef.current;
const currentNodes = nodes.map((node: NodeType) =>
_.cloneDeep(node.data.node.template)
_.cloneDeep(node.data.node?.template)
);
if (
tabsState &&

View file

@ -33,7 +33,7 @@ export default function CodeAreaComponent({
dynamic={dynamic}
value={myValue}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
setNodeClass={setNodeClass!}
setValue={(value: string) => {
setMyValue(value);
onChange(value);

View file

@ -29,7 +29,7 @@ import {
TabsTrigger,
} from "../../components/ui/tabs";
import { darkContext } from "../../contexts/darkContext";
import { FlowType } from "../../types/flow/index";
import { codeTabsPropsType } from "../../types/components";
import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
@ -40,28 +40,15 @@ export default function CodeTabsComponent({
setActiveTab,
isMessage,
tweaks,
}: {
flow?: FlowType;
tabs: any;
activeTab: string;
setActiveTab: any;
isMessage?: boolean;
tweaks?: {
tweak?: any;
tweaksList?: any;
buildContent?: any;
getValue?: any;
buildTweakObject?: any;
};
}) {
}: codeTabsPropsType) {
const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState([]);
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState<string[]>([]);
const { dark } = useContext(darkContext);
useEffect(() => {
if (flow && flow["data"]["nodes"]) {
setData(flow["data"]["nodes"]);
if (flow && flow["data"]!["nodes"]) {
setData(flow["data"]!["nodes"]);
}
}, [flow]);
@ -102,8 +89,8 @@ export default function CodeTabsComponent({
};
function openAccordions() {
let accordionsToOpen = [];
tweaks.tweak.current.forEach((el) => {
let accordionsToOpen: string[] = [];
tweaks?.tweak!.current.forEach((el) => {
Object.keys(el).forEach((key) => {
if (Object.keys(el[key]).length > 0) {
accordionsToOpen.push(key);
@ -205,9 +192,9 @@ export default function CodeTabsComponent({
: "overflow-hidden"
)}
>
{data.map((node: any, index) => (
{data?.map((node: any, index) => (
<div className="px-3" key={index}>
{tweaks.tweaksList.current.includes(
{tweaks?.tweaksList!.current.includes(
node["data"]["id"]
) && (
<AccordionComponent
@ -290,14 +277,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -310,7 +297,7 @@ export default function CodeTabsComponent({
templateField
].multiline ? (
<ShadTooltip
content={tweaks.buildContent(
content={tweaks.buildContent!(
node.data.node.template[
templateField
].value
@ -339,14 +326,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node
@ -384,14 +371,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -417,14 +404,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = e;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
e,
node.data.node.template[
@ -440,7 +427,7 @@ export default function CodeTabsComponent({
templateField
].type === "file" ? (
<ShadTooltip
content={tweaks.buildContent(
content={tweaks.buildContent!(
!node.data.node.template[
templateField
].value ||
@ -508,14 +495,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -544,14 +531,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -596,14 +583,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -617,7 +604,7 @@ export default function CodeTabsComponent({
templateField
].type === "prompt" ? (
<ShadTooltip
content={tweaks.buildContent(
content={tweaks.buildContent!(
!node.data.node.template[
templateField
].value ||
@ -651,14 +638,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -673,8 +660,8 @@ export default function CodeTabsComponent({
templateField
].type === "code" ? (
<ShadTooltip
content={tweaks.buildContent(
tweaks.getValue(
content={tweaks.buildContent!(
tweaks.getValue!(
node.data.node.template[
templateField
].value,
@ -706,14 +693,14 @@ export default function CodeTabsComponent({
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
newInputList![
index
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
tweaks.buildTweakObject!(
node["data"]["id"],
target,
node.data.node.template[
@ -742,7 +729,7 @@ export default function CodeTabsComponent({
</AccordionComponent>
)}
{tweaks.tweaksList.current.length === 0 && (
{tweaks?.tweaksList!.current.length === 0 && (
<>
<div className="pt-3">
No tweaks are available for this flow.

View file

@ -11,7 +11,7 @@ export default function Dropdown({
editNode = false,
numberOfOptions = 0,
apiModal = false,
}: DropDownComponentType) {
}: DropDownComponentType): JSX.Element {
let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value
);

View file

@ -7,7 +7,7 @@ export default function FloatComponent({
onChange,
disabled,
editNode = false,
}: FloatComponentType) {
}: FloatComponentType): JSX.Element {
const step = 0.1;
const min = 0;
const max = 1;

View file

@ -7,5 +7,11 @@ export default function IconComponent({
iconColor,
}: IconComponentProps): JSX.Element {
const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"];
return <TargetIcon className={className} style={{ color: iconColor }} />;
return (
<TargetIcon
strokeWidth={1.5}
className={className}
style={{ color: iconColor }}
/>
);
}

View file

@ -12,10 +12,11 @@ import { Link, useNavigate } from "react-router-dom";
import { alertContext } from "../../../../contexts/alertContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import FlowSettingsModal from "../../../../modals/flowSettingsModal";
import { menuBarPropsType } from "../../../../types/components";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
export const MenuBar = ({ flows, tabId }) => {
export const MenuBar = ({ flows, tabId }: menuBarPropsType): JSX.Element => {
const { addFlow } = useContext(TabsContext);
const { setErrorData } = useContext(alertContext);
const { undo, redo } = useContext(undoRedoContext);
@ -25,12 +26,12 @@ export const MenuBar = ({ flows, tabId }) => {
function handleAddFlow() {
try {
addFlow(null, true).then((id) => {
addFlow(undefined, true).then((id) => {
navigate("/flow/" + id);
});
// saveFlowStyleInDataBase();
} catch (err) {
setErrorData(err);
setErrorData(err as { title: string; list?: Array<string> });
}
}
let current_flow = flows.find((flow) => flow.id === tabId);
@ -45,7 +46,9 @@ export const MenuBar = ({ flows, tabId }) => {
<DropdownMenuTrigger asChild>
<Button asChild variant="primary" size="sm">
<div className="header-menu-bar-display">
<div className="header-menu-flow-name">{current_flow.name}</div>
<div className="header-menu-flow-name">
{current_flow!.name}
</div>
<IconComponent name="ChevronDown" className="h-4 w-4" />
</div>
</Button>

View file

@ -12,7 +12,7 @@ import { Button } from "../ui/button";
import { Separator } from "../ui/separator";
import MenuBar from "./components/menuBar";
export default function Header() {
export default function Header(): JSX.Element {
const { flows, tabId } = useContext(TabsContext);
const { dark, setDark } = useContext(darkContext);
const { notificationCenter } = useContext(alertContext);
@ -31,24 +31,12 @@ export default function Header() {
return (
<div className="header-arrangement">
<div className="header-start-display">
{tabId === "" || !tabId ? (
<div className="ml-2">
<a
href="https://www.langflow.org/"
target="_blank"
rel="noreferrer"
className="header-waitlist-link-box"
>
<span className="pr-1 text-2xl"></span>
<span>Join The Waitlist</span>
</a>
</div>
) : (
<Link to="/">
<span className="ml-4 text-2xl"></span>
</Link>
)}
<Link to="/">
<span className="ml-4 text-2xl"></span>
</Link>
<Button variant="outline" className="">
Sign out
</Button>
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
<MenuBar flows={flows} tabId={tabId} />
)}

View file

@ -1,3 +1,4 @@
import * as Form from "@radix-ui/react-form";
import { useEffect, useState } from "react";
import { InputComponentType } from "../../types/components";
import { classNames } from "../../utils/utils";
@ -7,9 +8,13 @@ export default function InputComponent({
value,
onChange,
disabled,
required = false,
isForm = false,
password,
editNode = false,
}: InputComponentType) {
placeholder = "Type something...",
className,
}: InputComponentType): JSX.Element {
const [pwdVisible, setPwdVisible] = useState(false);
// Clear component state
@ -21,20 +26,47 @@ export default function InputComponent({
return (
<div className="relative w-full">
<Input
value={value}
disabled={disabled}
className={classNames(
password && !pwdVisible && value !== "" ? " text-clip password " : "",
editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : ""
)}
placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(event) => {
onChange(event.target.value);
}}
/>
{isForm ? (
<Form.Control asChild>
<Input
value={value}
disabled={disabled}
required={required}
className={classNames(
password && !pwdVisible && value !== ""
? " text-clip password "
: "",
editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "",
className!
)}
placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => {
onChange(e.target.value);
}}
/>
</Form.Control>
) : (
<Input
value={value}
disabled={disabled}
required={required}
className={classNames(
password && !pwdVisible && value !== ""
? " text-clip password "
: "",
editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "",
className!
)}
placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => {
onChange(e.target.value);
}}
/>
)}
{password && (
<button
className={classNames(

View file

@ -13,7 +13,7 @@ export default function InputFileComponent({
fileTypes,
onFileChange,
editNode = false,
}: FileComponentType) {
}: FileComponentType): JSX.Element {
const [myValue, setMyValue] = useState(value);
const [loading, setLoading] = useState(false);
const { setErrorData } = useContext(alertContext);
@ -41,7 +41,7 @@ export default function InputFileComponent({
setMyValue(value);
}, [value]);
const handleButtonClick = () => {
const handleButtonClick = (): void => {
// Create a file input element
const input = document.createElement("input");
input.type = "file";
@ -49,7 +49,7 @@ export default function InputFileComponent({
input.style.display = "none"; // Hidden from view
input.multiple = false; // Allow only one file selection
input.onchange = (event: Event) => {
input.onchange = (event: Event): void => {
setLoading(true);
// Get the selected file

View file

@ -11,7 +11,7 @@ export default function InputListComponent({
onChange,
disabled,
editNode = false,
}: InputListComponentType) {
}: InputListComponentType): JSX.Element {
useEffect(() => {
if (disabled) {
onChange([""]);

View file

@ -7,7 +7,7 @@ export default function IntComponent({
onChange,
disabled,
editNode = false,
}: FloatComponentType) {
}: FloatComponentType): JSX.Element {
const min = 0;
// Clear component state

View file

@ -1,8 +1,8 @@
type LoadingComponentProps = {
remSize: number;
};
import { LoadingComponentProps } from "../../types/components";
export default function LoadingComponent({ remSize }: LoadingComponentProps) {
export default function LoadingComponent({
remSize,
}: LoadingComponentProps): JSX.Element {
return (
<div role="status" className="m-auto w-min">
<svg

View file

@ -14,7 +14,7 @@ export default function PromptAreaComponent({
onChange,
disabled,
editNode = false,
}: TextAreaComponentType) {
}: TextAreaComponentType): JSX.Element {
useEffect(() => {
if (disabled) {
onChange("");
@ -23,9 +23,9 @@ export default function PromptAreaComponent({
useEffect(() => {
if (value !== "" && !editNode) {
postValidatePrompt(field_name, value, nodeClass).then((apiReturn) => {
postValidatePrompt(field_name!, value, nodeClass!).then((apiReturn) => {
if (apiReturn.data) {
setNodeClass(apiReturn.data.frontend_node);
setNodeClass!(apiReturn.data.frontend_node);
// need to update reactFlowInstance to re-render the nodes.
}
});

View file

@ -10,7 +10,7 @@ export default function TextAreaComponent({
onChange,
disabled,
editNode = false,
}: TextAreaComponentType) {
}: TextAreaComponentType): JSX.Element {
// Clear text area
useEffect(() => {
if (disabled) {

View file

@ -7,7 +7,7 @@ export default function ToggleComponent({
enabled,
setEnabled,
disabled,
}: ToggleComponentType) {
}: ToggleComponentType): JSX.Element {
// set component state as disabled
useEffect(() => {
if (disabled) {

View file

@ -6,7 +6,7 @@ export default function ToggleShadComponent({
setEnabled,
disabled,
size,
}: ToggleComponentType) {
}: ToggleComponentType): JSX.Element {
let scaleX, scaleY;
switch (size) {
case "small":

View file

@ -27,4 +27,4 @@ const PopoverContent = React.forwardRef<
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent };
export { Popover, PopoverContent, PopoverTrigger };

View file

@ -23,14 +23,14 @@ export default function RenameLabel(props) {
});
if (inputRef.current) {
setTimeout(() => {
inputRef.current.focus();
inputRef.current?.focus();
}, 100);
}
}
resizeInput();
}, [isRename]);
const inputRef = useRef(null);
const inputRef = useRef<HTMLInputElement | null>(null);
const resizeInput = () => {
const input = inputRef.current;

View file

@ -0,0 +1,120 @@
"use client";
import * as SelectPrimitive from "@radix-ui/react-select";
import { Check, ChevronDown } from "lucide-react";
import * as React from "react";
import { cn } from "../../utils/utils";
const Select = SelectPrimitive.Root;
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
};

View file

@ -508,3 +508,93 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
"/api/v1/custom_component",
"/api/v1/validate/prompt",
];
export const CONTROL_INPUT_STATE = {
password: "",
cnfPassword: "",
username: "",
};
export const CONTROL_LOGIN_STATE = {
username: "",
password: "",
};
export const tabsCode = [];
export function tabsArray(codes: string[], method: number) {
if (!method) return;
if (method === 0) {
return [
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: codes[0],
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: codes[1],
},
{
name: "Python Code",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[2],
},
{
name: "Chat Widget HTML",
description:
"Insert this code anywhere in your &lt;body&gt; tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
mode: "html",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[3],
},
];
}
return [
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: codes[0],
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: codes[1],
},
{
name: "Python Code",
mode: "python",
language: "py",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
code: codes[2],
},
{
name: "Chat Widget HTML",
description:
"Insert this code anywhere in your &lt;body&gt; tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
mode: "html",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[3],
},
{
name: "Tweaks",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[4],
},
];
}

View file

@ -1,30 +1,9 @@
import { createContext, ReactNode, useState } from "react";
import { AlertItemType } from "../types/alerts";
import { alertContextType } from "../types/typesContext";
import _ from "lodash";
//types for alertContextType
type alertContextType = {
errorData: { title: string; list?: Array<string> };
setErrorData: (newState: { title: string; list?: Array<string> }) => void;
errorOpen: boolean;
setErrorOpen: (newState: boolean) => void;
noticeData: { title: string; link?: string };
setNoticeData: (newState: { title: string; link?: string }) => void;
noticeOpen: boolean;
setNoticeOpen: (newState: boolean) => void;
successData: { title: string };
setSuccessData: (newState: { title: string }) => void;
successOpen: boolean;
setSuccessOpen: (newState: boolean) => void;
notificationCenter: boolean;
setNotificationCenter: (newState: boolean) => void;
notificationList: Array<AlertItemType>;
pushNotificationList: (Object: AlertItemType) => void;
clearNotificationList: () => void;
removeFromNotificationList: (index: string) => void;
};
//initial values to alertContextType
const initialValue: alertContextType = {
errorData: { title: "", list: [] },
@ -65,7 +44,7 @@ export function AlertProvider({ children }: { children: ReactNode }) {
});
const [successOpen, setSuccessOpen] = useState(false);
const [notificationCenter, setNotificationCenter] = useState(false);
const [notificationList, setNotificationList] = useState([]);
const [notificationList, setNotificationList] = useState<AlertItemType[]>([]);
const pushNotificationList = (notification: AlertItemType) => {
setNotificationList((old) => {
let newNotificationList = _.cloneDeep(old);

View file

@ -0,0 +1,77 @@
import { createContext, useEffect, useState } from "react";
import { AuthContextType, userData } from "../types/contexts/auth";
const initialValue: AuthContextType = {
isAuthenticated: false,
accessToken: null,
login: () => {},
logout: () => {},
refreshAccessToken: () => Promise.resolve(),
userData: null,
setUserData: () => {},
};
const AuthContext = createContext<AuthContextType>(initialValue);
export function AuthProvider({ children }): React.ReactElement {
const [accessToken, setAccessToken] = useState<string | null>(null);
const [userData, setUserData] = useState<userData | null>(null);
useEffect(() => {
const storedAccessToken = localStorage.getItem("access_token");
if (storedAccessToken) {
setAccessToken(storedAccessToken);
}
}, []);
function login(newAccessToken: string, refreshToken: string) {
localStorage.setItem("access_token", newAccessToken);
setAccessToken(newAccessToken);
// Store refreshToken if needed
}
function logout() {
localStorage.removeItem("access_token");
// Clear refreshToken if used
setAccessToken(null);
}
async function refreshAccessToken(refreshToken: string) {
try {
// Call your API to refresh the access token using the refresh token
const response = await fetch("/api/refresh-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ refreshToken }),
});
if (response.ok) {
const data = await response.json();
login(data.accessToken, refreshToken);
} else {
logout();
}
} catch (error) {
logout();
}
}
return (
// !! to convert string to boolean
<AuthContext.Provider
value={{
isAuthenticated: !!accessToken,
accessToken,
login,
logout,
refreshAccessToken,
setUserData,
userData,
}}
>
{children}
</AuthContext.Provider>
);
}

View file

@ -1,9 +1,5 @@
import { createContext, useEffect, useState } from "react";
type darkContextType = {
dark: {};
setDark: (newState: {}) => void;
};
import { darkContextType } from "../types/typesContext";
const initialValue = {
dark: {},
@ -14,13 +10,13 @@ export const darkContext = createContext<darkContextType>(initialValue);
export function DarkProvider({ children }) {
const [dark, setDark] = useState(
JSON.parse(window.localStorage.getItem("isDark")) ?? false
JSON.parse(window.localStorage.getItem("isDark")!) ?? false
);
useEffect(() => {
if (dark) {
document.getElementById("body").classList.add("dark");
document.getElementById("body")!.classList.add("dark");
} else {
document.getElementById("body").classList.remove("dark");
document.getElementById("body")!.classList.remove("dark");
}
window.localStorage.setItem("isDark", dark.toString());
}, [dark]);

View file

@ -1,34 +1,5 @@
import { createContext, ReactNode, useState } from "react";
//types for location context
type locationContextType = {
current: Array<string>;
setCurrent: (newState: Array<string>) => void;
isStackedOpen: boolean;
setIsStackedOpen: (newState: boolean) => void;
showSideBar: boolean;
setShowSideBar: (newState: boolean) => void;
extraNavigation: {
title: string;
options?: Array<{
name: string;
href: string;
icon: any;
children?: Array<any>;
}>;
};
setExtraNavigation: (newState: {
title: string;
options?: Array<{
name: string;
href: string;
icon: any;
children?: Array<any>;
}>;
}) => void;
extraComponent: any;
setExtraComponent: (newState: any) => void;
};
import { locationContextType } from "../types/typesContext";
//initial value for location context
const initialValue = {

View file

@ -7,7 +7,7 @@ import {
useRef,
useState,
} from "react";
import { addEdge } from "reactflow";
import { Edge, Node, ReactFlowJsonObject, addEdge } from "reactflow";
import ShortUniqueId from "short-unique-id";
import {
deleteFlowFromDatabase,
@ -18,8 +18,9 @@ import {
uploadFlowsToDatabase,
} from "../controllers/API";
import { APIClassType, APITemplateType } from "../types/api";
import { FlowType, NodeType } from "../types/flow";
import { TabsContextType, TabsState } from "../types/tabs";
import { tweakType } from "../types/components";
import { FlowType, NodeDataType, NodeType } from "../types/flow";
import { TabsContextType, TabsState, errorsVarType } from "../types/tabs";
import {
addVersionToDuplicates,
updateIds,
@ -73,9 +74,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uid());
const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const [tabsState, setTabsState] = useState<TabsState>({});
const [getTweak, setTweak] = useState([]);
const [getTweak, setTweak] = useState<tweakType>([]);
const newNodeId = useRef(uid());
function incrementNodeId() {
@ -130,8 +134,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
//get tabs from db
return readFlowsFromDatabase();
}
function processDBData(DbData) {
DbData.forEach((flow) => {
function processDBData(DbData: FlowType[]) {
DbData.forEach((flow: FlowType) => {
try {
if (!flow.data) {
return;
@ -144,7 +148,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
}
function processFlowEdges(flow) {
function processFlowEdges(flow: FlowType) {
if (!flow.data || !flow.data.edges) return;
flow.data.edges.forEach((edge) => {
edge.className = "";
@ -153,14 +157,14 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
function updateDisplay_name(node: NodeType, template: APIClassType) {
node.data.node.display_name = template["display_name"] || node.data.type;
node.data.node!.display_name = template["display_name"] || node.data.type;
}
function updateNodeDocumentation(node: NodeType, template: APIClassType) {
node.data.node.documentation = template["documentation"];
node.data.node!.documentation = template["documentation"];
}
function processFlowNodes(flow) {
function processFlowNodes(flow: FlowType) {
if (!flow.data || !flow.data.nodes) return;
flow.data.nodes.forEach((node: NodeType) => {
const template = templates[node.data.type];
@ -180,7 +184,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
node.data.node.base_classes = template["base_classes"];
node.data.node!.base_classes = template["base_classes"];
}
function updateNodeEdges(
@ -188,10 +192,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
node: NodeType,
template: APIClassType
) {
flow.data.edges.forEach((edge) => {
flow.data!.edges.forEach((edge) => {
if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle
.split("|")
?.split("|")
.slice(0, 2)
.concat(template["base_classes"])
.join("|");
@ -200,17 +204,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
function updateNodeDescription(node: NodeType, template: APIClassType) {
node.data.node.description = template["description"];
node.data.node!.description = template["description"];
}
function updateNodeTemplate(node: NodeType, template: APIClassType) {
node.data.node.template = updateTemplate(
node.data.node!.template = updateTemplate(
template["template"] as unknown as APITemplateType,
node.data.node.template as APITemplateType
node.data.node!.template as APITemplateType
);
}
function updateStateWithDbData(tabsData) {
function updateStateWithDbData(tabsData: FlowType[]) {
setFlows(tabsData);
}
@ -241,7 +245,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
link.download = `${
flowName && flowName != ""
? flowName
: flows.find((f) => f.id === tabId).name
: flows.find((f) => f.id === tabId)!.name
}.json`;
// simulate a click on the link element to trigger the download
@ -293,10 +297,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
input.onchange = (e: Event) => {
// check if the file type is application/json
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];
// read the file as text
currentfile.text().then((text) => {
// parse the text into a JSON object
@ -319,10 +323,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
input.onchange = (event: Event) => {
// check if the file type is application/json
if (
(event.target as HTMLInputElement).files[0].type === "application/json"
(event.target as HTMLInputElement).files![0].type === "application/json"
) {
// get the file from the file input
const file = (event.target as HTMLInputElement).files[0];
const file = (event.target as HTMLInputElement).files![0];
// read the file as text
const formData = new FormData();
formData.append("file", file);
@ -353,15 +357,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
*/
function paste(
selectionInstance,
selectionInstance: { nodes: Node[]; edges: Edge[] },
position: { x: number; y: number; paneX?: number; paneY?: number }
) {
let minimumX = Infinity;
let minimumY = Infinity;
let idsMap = {};
let nodes = reactFlowInstance.getNodes();
let edges = reactFlowInstance.getEdges();
selectionInstance.nodes.forEach((node) => {
let nodes: Node<NodeDataType>[] = reactFlowInstance!.getNodes();
let edges = reactFlowInstance!.getEdges();
selectionInstance.nodes.forEach((node: Node) => {
if (node.position.y < minimumY) {
minimumY = node.position.y;
}
@ -371,8 +375,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
const insidePosition = position.paneX
? { x: position.paneX + position.x, y: position.paneY + position.y }
: reactFlowInstance.project({ x: position.x, y: position.y });
? { x: position.paneX + position.x, y: position.paneY! + position.y }
: reactFlowInstance!.project({ x: position.x, y: position.y });
selectionInstance.nodes.forEach((node: NodeType) => {
// Generate a unique node ID
@ -384,8 +388,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
id: newId,
type: "genericNode",
position: {
x: insidePosition.x + node.position.x - minimumX,
y: insidePosition.y + node.position.y - minimumY,
x: insidePosition.x + node.position!.x - minimumX,
y: insidePosition.y + node.position!.y - minimumY,
},
data: {
..._.cloneDeep(node.data),
@ -398,19 +402,19 @@ export function TabsProvider({ children }: { children: ReactNode }) {
.map((node) => ({ ...node, selected: false }))
.concat({ ...newNode, selected: false });
});
reactFlowInstance.setNodes(nodes);
reactFlowInstance!.setNodes(nodes);
selectionInstance.edges.forEach((edge) => {
let source = idsMap[edge.source];
let target = idsMap[edge.target];
let sourceHandleSplitted = edge.sourceHandle.split("|");
let sourceHandleSplitted = edge.sourceHandle!.split("|");
let sourceHandle =
sourceHandleSplitted[0] +
"|" +
source +
"|" +
sourceHandleSplitted.slice(2).join("|");
let targetHandleSplitted = edge.targetHandle.split("|");
let targetHandleSplitted = edge.targetHandle!.split("|");
let targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + target;
let id =
@ -438,21 +442,21 @@ export function TabsProvider({ children }: { children: ReactNode }) {
edges.map((edge) => ({ ...edge, selected: false }))
);
});
reactFlowInstance.setEdges(edges);
reactFlowInstance!.setEdges(edges);
}
const addFlow = async (
flow?: FlowType,
newProject?: Boolean
): Promise<String> => {
): Promise<String | undefined> => {
if (newProject) {
let flowData = extractDataFromFlow(flow);
let flowData = extractDataFromFlow(flow!);
if (flowData.description == "") {
flowData.description = getRandomDescription();
}
// Create a new flow with a default name if no flow is provided.
const newFlow = createNewFlow(flowData, flow);
const newFlow = createNewFlow(flowData, flow!);
processFlowEdges(newFlow);
processFlowNodes(newFlow);
@ -477,13 +481,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
} else {
paste(
{ nodes: flow.data.nodes, edges: flow.data.edges },
{ nodes: flow!.data!.nodes, edges: flow!.data!.edges },
{ x: 10, y: 10 }
);
}
};
const extractDataFromFlow = (flow) => {
const extractDataFromFlow = (flow: FlowType) => {
let data = flow?.data ? flow.data : null;
const description = flow?.description ? flow.description : "";
@ -496,17 +500,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
return { data, description };
};
const updateEdges = (edges) => {
const updateEdges = (edges: Edge[]) => {
edges.forEach((edge) => {
edge.className =
(edge.targetHandle.split("|")[0] === "Text"
(edge.targetHandle!.split("|")[0] === "Text"
? "stroke-gray-800 "
: "stroke-gray-900 ") + " stroke-connection";
edge.animated = edge.targetHandle.split("|")[0] === "Text";
edge.animated = edge.targetHandle!.split("|")[0] === "Text";
});
};
const updateNodes = (nodes, edges) => {
const updateNodes = (nodes: Node[], edges: Edge[]) => {
nodes.forEach((node) => {
const template = templates[node.data.type];
if (!template) {
@ -517,8 +521,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
node.data.node.base_classes = template["base_classes"];
edges.forEach((edge) => {
if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle
.split("|")
edge.sourceHandle = edge
.sourceHandle!.split("|")
.slice(0, 2)
.concat(template["base_classes"])
.join("|");
@ -533,14 +537,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
};
const createNewFlow = (flowData, flow) => ({
const createNewFlow = (
flowData: { data: ReactFlowJsonObject | null; description: string },
flow: FlowType
) => ({
description: flowData.description,
name: flow?.name ?? getRandomName(),
data: flowData.data,
id: "",
});
const addFlowToLocalState = (newFlow) => {
const addFlowToLocalState = (newFlow: FlowType) => {
setFlows((prevState) => {
return [...prevState, newFlow];
});
@ -591,7 +598,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
}
} catch (err) {
setErrorData(err);
setErrorData(err as errorsVarType);
}
}

View file

@ -1,5 +1,5 @@
import { createContext, ReactNode, useEffect, useState } from "react";
import { Node } from "reactflow";
import { Node, ReactFlowInstance } from "reactflow";
import { getAll } from "../controllers/API";
import { APIKindType } from "../types/api";
import { typesContextType } from "../types/typesContext";
@ -8,7 +8,7 @@ import { typesContextType } from "../types/typesContext";
const initialValue: typesContextType = {
reactFlowInstance: null,
setReactFlowInstance: () => {},
setReactFlowInstance: (newState: ReactFlowInstance) => {},
deleteNode: () => {},
types: {},
setTypes: () => {},
@ -22,13 +22,14 @@ export const typesContext = createContext<typesContextType>(initialValue);
export function TypesProvider({ children }: { children: ReactNode }) {
const [types, setTypes] = useState({});
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const [reactFlowInstance, setReactFlowInstance] =
useState<ReactFlowInstance | null>(null);
const [templates, setTemplates] = useState({});
const [data, setData] = useState({});
useEffect(() => {
let delay = 1000; // Start delay of 1 second
let intervalId = null;
let intervalId: NodeJS.Timer;
let retryCount = 0; // Count of retry attempts
const maxRetryCount = 5; // Max retry attempts
@ -69,7 +70,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
);
}
// Clear the interval if successful.
clearInterval(intervalId);
clearInterval(intervalId!);
} catch (error) {
console.error("An error has occurred while fetching types.");
}
@ -77,21 +78,20 @@ export function TypesProvider({ children }: { children: ReactNode }) {
// Start the initial interval.
intervalId = setInterval(getTypes, delay);
return () => {
// This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change.
clearInterval(intervalId);
clearInterval(intervalId!);
// Indicate that the component has been unmounted.
isMounted = false;
};
}, []);
function deleteNode(idx: string) {
reactFlowInstance.setNodes(
reactFlowInstance.getNodes().filter((node: Node) => node.id !== idx)
reactFlowInstance!.setNodes(
reactFlowInstance!.getNodes().filter((node: Node) => node.id !== idx)
);
reactFlowInstance.setEdges(
reactFlowInstance
reactFlowInstance!.setEdges(
reactFlowInstance!
.getEdges()
.filter((edge) => edge.source !== idx && edge.target !== idx)
);

View file

@ -6,26 +6,15 @@ import {
useEffect,
useState,
} from "react";
import { Edge, Node, useReactFlow } from "reactflow";
import { useReactFlow } from "reactflow";
import {
HistoryItem,
UseUndoRedoOptions,
undoRedoContextType,
} from "../types/typesContext";
import { isWrappedWithClass } from "../utils/utils";
import { TabsContext } from "./tabsContext";
type undoRedoContextType = {
undo: () => void;
redo: () => void;
takeSnapshot: () => void;
};
type UseUndoRedoOptions = {
maxHistorySize: number;
enableShortcuts: boolean;
};
type HistoryItem = {
nodes: Node[];
edges: Edge[];
};
const initialValue = {
undo: () => {},
redo: () => {},

View file

@ -1,6 +1,5 @@
import axios, { AxiosError, AxiosInstance } from "axios";
import { useContext, useEffect, useRef } from "react";
import { URL_EXCLUDED_FROM_ERROR_RETRIES } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
// Create a new Axios instance
@ -8,7 +7,7 @@ const api: AxiosInstance = axios.create({
baseURL: "",
});
function ApiInterceptor() {
function ApiInterceptor(): null {
const retryCounts = useRef([]);
const { setErrorData } = useContext(alertContext);
@ -16,32 +15,31 @@ function ApiInterceptor() {
const interceptor = api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {
return Promise.reject(error);
}
let retryCount = 0;
while (retryCount < 4) {
await sleep(5000); // Sleep for 5 seconds
retryCount++;
try {
const response = await axios.request(error.config);
return response;
} catch (error) {
if (retryCount === 3) {
setErrorData({
title: "There was an error on web connection, please: ",
list: [
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
"Endpoint: " + error.config?.url,
],
});
return Promise.reject(error);
}
}
}
// if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {
// return Promise.reject(error);
// }
// let retryCount = 0;
// while (retryCount < 4) {
// await sleep(5000); // Sleep for 5 seconds
// retryCount++;
// try {
// const response = await axios.request(error.config);
// return response;
// } catch (error) {
// if (retryCount === 3) {
// setErrorData({
// title: "There was an error on web connection, please: ",
// list: [
// "Refresh the page",
// "Use a new flow tab",
// "Check if the backend is up",
// "Endpoint: " + error.config?.url,
// ],
// });
// return Promise.reject(error);
// }
// }
// }
}
);
@ -55,7 +53,7 @@ function ApiInterceptor() {
}
// Function to sleep for a given duration in milliseconds
function sleep(ms) {
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View file

@ -23,7 +23,7 @@ export async function getAll(): Promise<AxiosResponse<APIObjectType>> {
const GITHUB_API_URL = "https://api.github.com";
export async function getRepoStars(owner, repo) {
export async function getRepoStars(owner: string, repo: string) {
try {
const response = await api.get(`${GITHUB_API_URL}/repos/${owner}/${repo}`);
return response.data.stargazers_count;
@ -100,7 +100,7 @@ export async function getExamples(): Promise<FlowType[]> {
export async function saveFlowToDatabase(newFlow: {
name: string;
id: string;
data: ReactFlowJsonObject;
data: ReactFlowJsonObject | null;
description: string;
style?: FlowStyleType;
}): Promise<FlowType> {
@ -179,7 +179,7 @@ export async function downloadFlowsFromDatabase() {
}
}
export async function uploadFlowsToDatabase(flows) {
export async function uploadFlowsToDatabase(flows: FormData) {
try {
const response = await api.post(`/api/v1/flows/upload/`, flows);

View file

@ -1,9 +1,9 @@
import React, { forwardRef } from "react";
import { ReactComponent as PowerPointSVG } from "./PowerPoint.svg";
import SvgPowerPoint from "./PowerPoint";
export const PowerPointIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
return <PowerPointSVG ref={ref} {...props} />;
return <SvgPowerPoint ref={ref} {...props} />;
});

View file

@ -15,7 +15,9 @@ import CodeTabsComponent from "../../components/codeTabsComponent";
import IconComponent from "../../components/genericIconComponent";
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext";
import { FlowType } from "../../types/flow/index";
import { TemplateVariableType } from "../../types/api";
import { tweakType, uniqueTweakType } from "../../types/components";
import { FlowType, NodeType } from "../../types/flow/index";
import { buildTweaks } from "../../utils/reactflowUtils";
import {
getCurlCode,
@ -41,8 +43,8 @@ const ApiModal = forwardRef(
) => {
const [open, setOpen] = useState(false);
const [activeTab, setActiveTab] = useState("0");
const tweak = useRef([]);
const tweaksList = useRef([]);
const tweak = useRef<tweakType>([]);
const tweaksList = useRef<string[]>([]);
const { setTweak, getTweak, tabsState } = useContext(TabsContext);
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
const curl_code = getCurlCode(flow, tweak.current, tabsState);
@ -65,7 +67,7 @@ const ApiModal = forwardRef(
}
useEffect(() => {
if (flow["data"]["nodes"].length == 0) {
if (flow["data"]!["nodes"].length == 0) {
startState();
} else {
tweak.current = [];
@ -81,12 +83,12 @@ const ApiModal = forwardRef(
} else {
setTabs(tabsArray(codesArray, 1));
}
}, [flow["data"]["nodes"], open]);
}, [flow["data"]!["nodes"], open]);
function filterNodes() {
let arrNodesWithValues = [];
let arrNodesWithValues: string[] = [];
flow["data"]["nodes"].forEach((node) => {
flow["data"]!["nodes"].forEach((node) => {
Object.keys(node["data"]["node"]["template"])
.filter(
(templateField) =>
@ -109,11 +111,15 @@ const ApiModal = forwardRef(
return self.indexOf(value) === index;
});
}
function buildTweakObject(tw, changes, template) {
if (template.type === "float") {
function buildTweakObject(
tw: string,
changes: string | string[] | boolean | number,
template: TemplateVariableType
) {
if (typeof changes === "string" && template.type === "float") {
changes = parseFloat(changes);
}
if (template.type === "int") {
if (typeof changes === "string" && template.type === "int") {
changes = parseInt(changes);
}
if (template.list === true && Array.isArray(changes)) {
@ -125,7 +131,7 @@ const ApiModal = forwardRef(
);
if (existingTweak) {
existingTweak[tw][template["name"]] = changes;
existingTweak[tw][template["name"]] = changes as string;
if (existingTweak[tw][template["name"]] == template.value) {
tweak.current.forEach((element) => {
@ -142,7 +148,7 @@ const ApiModal = forwardRef(
[tw]: {
[template["name"]]: changes,
},
};
} as uniqueTweakType;
tweak.current.push(newTweak);
}
@ -151,15 +157,15 @@ const ApiModal = forwardRef(
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
const widgetCode = getWidgetCode(flow, tabsState);
tabs[0].code = curl_code;
tabs[1].code = pythonApiCode;
tabs[2].code = pythonCode;
tabs[3].code = widgetCode;
tabs![0].code = curl_code;
tabs![1].code = pythonApiCode;
tabs![2].code = pythonCode;
tabs![3].code = widgetCode;
setTweak(tweak.current);
}
function buildContent(value) {
function buildContent(value: string) {
const htmlContent = (
<div className="w-[200px]">
<span>{value != null && value != "" ? value : "None"}</span>
@ -168,7 +174,11 @@ const ApiModal = forwardRef(
return htmlContent;
}
function getValue(value, node, template) {
function getValue(
value: string,
node: NodeType,
template: TemplateVariableType
) {
let returnValue = value ?? "";
if (getTweak.length > 0) {
@ -204,7 +214,7 @@ const ApiModal = forwardRef(
<BaseModal.Content>
<CodeTabsComponent
flow={flow}
tabs={tabs}
tabs={tabs!}
activeTab={activeTab}
setActiveTab={setActiveTab}
tweaks={{

View file

@ -0,0 +1,66 @@
import { useState } from "react";
import { Button } from "../../components/ui/button";
import { ConfirmationModalType } from "../../types/components";
import { nodeIconsLucide } from "../../utils/styleUtils";
import BaseModal from "../baseModal";
export default function ConfirmationModal({
title,
titleHeader,
modalContent,
modalContentTitle,
cancelText,
confirmationText,
children,
icon,
data,
index,
onConfirm,
}: ConfirmationModalType) {
const Icon: any = nodeIconsLucide[icon];
const [open, setOpen] = useState(false);
return (
<BaseModal size="x-small" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={titleHeader}>
<span className="pr-2">{title}</span>
<Icon
name="icon"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
{modalContentTitle != "" && (
<>
<strong>{modalContentTitle}</strong>
<br></br>
</>
)}
<span>{modalContent}</span>
</BaseModal.Content>
<BaseModal.Footer>
<Button
className="ml-3"
onClick={() => {
setOpen(false);
onConfirm(index, data);
}}
>
{confirmationText}
</Button>
<Button
variant="outline"
onClick={() => {
setOpen(false);
}}
>
{cancelText}
</Button>
</BaseModal.Footer>
</BaseModal>
);
}

View file

@ -25,6 +25,7 @@ import { limitScrollFieldsModal } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
import { TabsState } from "../../types/tabs";
import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal";
@ -53,19 +54,22 @@ const EditNodeModal = forwardRef(
?.getEdges()
.some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(templateParam) {
function changeAdvanced(templateParam: string): void {
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[templateParam].advanced =
!newData.node.template[templateParam].advanced;
newData.node!.template[templateParam].advanced =
!newData.node!.template[templateParam].advanced;
return newData;
});
}
const handleOnNewValue = (newValue: any, name) => {
const handleOnNewValue = (
newValue: string | string[] | boolean,
name: string
) => {
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[name].value = newValue;
newData.node!.template[name].value = newValue;
return newData;
});
};
@ -77,7 +81,7 @@ const EditNodeModal = forwardRef(
return (
<BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={myData.node?.description}>
<BaseModal.Header description={myData.node?.description!}>
<span className="pr-2">{myData.type}</span>
<Badge variant="secondary">ID: {myData.id}</Badge>
</BaseModal.Header>
@ -112,11 +116,11 @@ const EditNodeModal = forwardRef(
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(myData.node.template)
{Object.keys(myData.node!.template)
.filter(
(templateParam) =>
templateParam.charAt(0) !== "_" &&
myData.node.template[templateParam].show &&
myData.node?.template[templateParam].show &&
(myData.node.template[templateParam].type ===
"str" ||
myData.node.template[templateParam].type ===
@ -135,13 +139,13 @@ const EditNodeModal = forwardRef(
.map((templateParam, index) => (
<TableRow key={index} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
{myData.node.template[templateParam].name
{myData.node?.template[templateParam].name
? myData.node.template[templateParam].name
: myData.node.template[templateParam]
: myData.node?.template[templateParam]
.display_name}
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node.template[templateParam].type ===
{myData.node?.template[templateParam].type ===
"str" &&
!myData.node.template[templateParam].options ? (
<div className="mx-auto">
@ -171,7 +175,7 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
/>
@ -193,7 +197,7 @@ const EditNodeModal = forwardRef(
/>
)}
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"bool" ? (
<div className="ml-auto">
{" "}
@ -211,7 +215,7 @@ const EditNodeModal = forwardRef(
size="small"
/>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"float" ? (
<div className="mx-auto">
<FloatComponent
@ -226,7 +230,7 @@ const EditNodeModal = forwardRef(
}}
/>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"str" &&
myData.node.template[templateParam].options ? (
<div className="mx-auto">
@ -246,7 +250,7 @@ const EditNodeModal = forwardRef(
}
></Dropdown>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"int" ? (
<div className="mx-auto">
<IntComponent
@ -261,7 +265,7 @@ const EditNodeModal = forwardRef(
}}
/>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"file" ? (
<div className="mx-auto">
<InputFileComponent
@ -271,7 +275,7 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
fileTypes={
@ -283,13 +287,13 @@ const EditNodeModal = forwardRef(
.suffixes
}
onFileChange={(filePath: string) => {
data.node.template[
data.node!.template[
templateParam
].file_path = filePath;
}}
></InputFileComponent>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
@ -304,17 +308,17 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"code" ? (
<div className="mx-auto">
<CodeAreaComponent
dynamic={
data.node.template[templateParam]
data.node!.template[templateParam]
.dynamic ?? false
}
setNodeClass={(nodeClass) => {
@ -327,12 +331,12 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node.template[templateParam].type ===
) : myData.node?.template[templateParam].type ===
"Any" ? (
"-"
) : (
@ -343,7 +347,7 @@ const EditNodeModal = forwardRef(
<div className="items-center text-center">
<ToggleShadComponent
enabled={
!myData.node.template[templateParam]
!myData.node?.template[templateParam]
.advanced
}
setEnabled={(e) =>
@ -369,7 +373,8 @@ const EditNodeModal = forwardRef(
className="mt-3"
onClick={() => {
setData(cloneDeep(myData)); //saves data with actual state of modal
setTabsState((prev) => {
//@ts-ignore
setTabsState((prev: TabsState) => {
return {
...prev,
[tabId]: {

View file

@ -1,180 +0,0 @@
import { useState } from "react";
import CodeAreaComponent from "../../../../components/codeAreaComponent";
import Dropdown from "../../../../components/dropdownComponent";
import FloatComponent from "../../../../components/floatComponent";
import InputComponent from "../../../../components/inputComponent";
import InputFileComponent from "../../../../components/inputFileComponent";
import InputListComponent from "../../../../components/inputListComponent";
import IntComponent from "../../../../components/intComponent";
import PromptAreaComponent from "../../../../components/promptComponent";
import TextAreaComponent from "../../../../components/textAreaComponent";
import ToggleComponent from "../../../../components/toggleComponent";
import { classNames } from "../../../../utils/utils";
export default function ModalField({
data,
title,
required,
id,
name,
type,
index,
}) {
const [enabled, setEnabled] = useState(
data.node.template[name]?.value ?? false
);
const display =
type === "str" ||
type === "int" ||
type === "prompt" ||
type === "bool" ||
type === "float" ||
type === "file" ||
type === "code";
return (
<div
className={classNames(
"flex w-full flex-row items-center justify-between",
display ? "" : "hidden",
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].advanced &&
data.node.template[t].show
).length -
1 ===
index
? "pb-4"
: ""
)}
>
{display && (
<div>
<span className="mx-2">{title}</span>
<span className="text-status-red">{required ? " *" : ""}</span>
</div>
)}
{type === "str" && !data.node.template[name].options ? (
<div className="w-1/2">
{data.node.template[name].list ? (
<InputListComponent
disabled={false}
value={
!data.node.template[name].value ||
data.node.template[name].value === ""
? [""]
: data.node.template[name].value
}
onChange={(t: string[]) => {
data.node.template[name].value = t;
}}
/>
) : data.node.template[name].multiline ? (
<TextAreaComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
/>
) : (
<InputComponent
disabled={false}
password={data.node.template[name].password ?? false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
)}
</div>
) : type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleComponent
disabled={false}
enabled={enabled}
setEnabled={(t) => {
data.node.template[name].value = t;
setEnabled(t);
}}
size="small"
/>
</div>
) : type === "float" ? (
<div className="w-1/2">
<FloatComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
</div>
) : type === "str" && data.node.template[name].options ? (
<div className="w-1/2">
<Dropdown
options={data.node.template[name].options}
onSelect={(newValue) => (data.node.template[name].value = newValue)}
value={data.node.template[name].value ?? "Choose an option"}
></Dropdown>
</div>
) : type === "int" ? (
<div className="w-1/2">
<IntComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t) => {
data.node.template[name].value = t;
}}
/>
</div>
) : type === "file" ? (
<div className="w-1/2">
<InputFileComponent
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
fileTypes={data.node.template[name].fileTypes}
suffixes={data.node.template[name].suffixes}
onFileChange={(t: string) => {
data.node.template[name].file_path = t;
}}
></InputFileComponent>
</div>
) : type === "prompt" ? (
<div className="w-1/2">
<PromptAreaComponent
field_name={name}
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
/>
</div>
) : type === "code" ? (
<div className="w-1/2">
<CodeAreaComponent
dynamic={data.node.template[name].dynamic ?? false}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {
data.node.template[name].value = t;
}}
/>
</div>
) : (
<div className="hidden"></div>
)}
</div>
);
}

View file

@ -1,151 +0,0 @@
import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useContext, useRef, useState } from "react";
import IconComponent from "../../components/genericIconComponent";
import { limitScrollFieldsModal } from "../../constants/constants";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, toTitleCase } from "../../utils/utils";
import ModalField from "./components/ModalField";
export default function NodeModal({ data }: { data: NodeDataType }) {
const [modalOpen, setModalOpen] = useState(false);
const { types } = useContext(typesContext);
const ref = useRef();
// any to avoid type conflict
const Icon: any = nodeIconsLucide[types[data.type]];
return (
<Dialog
open={modalOpen}
as="div"
className="relative z-10"
onClose={setModalOpen}
initialFocus={ref}
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="node-modal-div" />
</Transition.Child>
<div className="node-modal-dialog-arrangement">
<div className="node-modal-dialog-div">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="node-modal-dialog-panel">
<div className=" node-modal-dialog-panel-div">
<button
type="button"
className="node-modal-dialog-button"
onClick={() => {
setModalOpen(false);
}}
>
<span className="sr-only">Close</span>
<IconComponent
name="X"
className="h-6 w-6"
aria-hidden="true"
/>
</button>
</div>
<div className="node-modal-dialog-icon-div">
<div className="node-modal-icon-arrangement">
<Icon
strokeWidth={1.5}
className="node-modal-icon"
style={{
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}}
/>
<div className="node-modal-title-div">
<Dialog.Title as="h3" className="node-modal-title">
{data.type}
</Dialog.Title>
</div>
</div>
<div className="node-modal-template-div">
<div className="flex-max-width h-[445px]">
<div
className={classNames(
"node-modal-template",
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].advanced &&
data.node.template[t].show
).length > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll"
: "overflow-hidden"
)}
>
<div className="node-modal-template-column">
{Object.keys(data.node.template)
.filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].advanced &&
data.node.template[t].show
)
.map((t: string, idx) => {
return (
<ModalField
key={idx}
data={data}
title={
data.node.template[t].display_name
? data.node.template[t].display_name
: data.node.template[t].name
? toTitleCase(data.node.template[t].name)
: toTitleCase(t)
}
required={data.node.template[t].required}
id={
data.node.template[t].type +
"|" +
t +
"|" +
data.id
}
name={t}
type={data.node.template[t].type}
index={idx}
/>
);
})}
</div>
</div>
</div>
</div>
<div className="node-modal-button-box">
<button
type="button"
className="node-modal-button"
onClick={() => {
setModalOpen(false);
}}
>
Done
</button>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
);
}

View file

@ -0,0 +1,241 @@
import * as Form from "@radix-ui/react-form";
import { useEffect, useState } from "react";
import InputComponent from "../../components/inputComponent";
import { Button } from "../../components/ui/button";
import { UserManagementType } from "../../types/components";
import { nodeIconsLucide } from "../../utils/styleUtils";
import BaseModal from "../baseModal";
export default function UserManagementModal({
title,
titleHeader,
cancelText,
confirmationText,
children,
icon,
data,
index,
onConfirm,
}: UserManagementType) {
const Icon: any = nodeIconsLucide[icon];
const [open, setOpen] = useState(false);
const [password, setPassword] = useState(data?.password ?? "");
const [username, setUserName] = useState(data?.user ?? "");
const [confirmPassword, setConfirmPassword] = useState(data?.password ?? "");
useEffect(() => {
if (!data) {
resetForm();
}
}, [data, open]);
function resetForm() {
setPassword("");
setUserName("");
setConfirmPassword("");
}
return (
<BaseModal size="medium-h-full" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={titleHeader}>
<span className="pr-2">{title}</span>
<Icon
name="icon"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<Form.Root
onSubmit={(event) => {
if (password !== confirmPassword) {
event.preventDefault();
return;
}
const data = Object.fromEntries(new FormData(event.currentTarget));
resetForm();
onConfirm(index ?? -1, data);
setOpen(false);
event.preventDefault();
}}
>
<div className="grid gap-5">
<Form.Field name="username">
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="data-[invalid]:label-invalid">
Username{" "}
<span className="font-medium text-destructive">*</span>
</Form.Label>
</div>
<Form.Control asChild>
<input
onChange={(input) => {
setUserName(input.target.value);
}}
value={username}
className="primary-input"
required
placeholder="Username"
/>
</Form.Control>
<Form.Message match="valueMissing" className="field-invalid">
Please enter your username
</Form.Message>
</Form.Field>
<div className="flex flex-row">
<div className="mr-3 basis-1/2">
<Form.Field
name="password"
serverInvalid={password != confirmPassword}
>
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="data-[invalid]:label-invalid">
Password{" "}
<span className="font-medium text-destructive">*</span>
</Form.Label>
</div>
<InputComponent
onChange={(input) => {
setPassword(input);
}}
value={password}
password={true}
isForm
className="primary-input"
required
placeholder="Password"
/>
<Form.Message className="field-invalid" match="valueMissing">
Please enter a password
</Form.Message>
{password != confirmPassword && (
<Form.Message className="field-invalid">
Passwords do not match
</Form.Message>
)}
</Form.Field>
</div>
<div className="basis-1/2">
<Form.Field
name="confirmpassword"
serverInvalid={password != confirmPassword}
>
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="data-[invalid]:label-invalid">
Confirm password{" "}
<span className="font-medium text-destructive">*</span>
</Form.Label>
</div>
<InputComponent
onChange={(input) => {
setConfirmPassword(input);
}}
value={confirmPassword}
password={true}
isForm
className="primary-input"
required
placeholder="Confirm your password"
/>
<Form.Message className="field-invalid" match="valueMissing">
Please confirm your password
</Form.Message>
</Form.Field>
</div>
</div>
{/*
<Form.Field name="email">
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="data-[invalid]:label-invalid">
Email <span className="font-medium text-destructive">*</span>
</Form.Label>
<Form.Message className="field-invalid" match="valueMissing">
Please enter your email
</Form.Message>
<Form.Message className="field-invalid" match="typeMismatch">
Please provide a valid email
</Form.Message>
</div>
<Form.Control asChild>
<input className="primary-input" type="email" required />
</Form.Control>
</Form.Field> */}
{/*
<Form.Field name="birth">
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="data-[invalid]:label-invalid">
Date of birth{" "}
<span className="font-medium text-destructive">*</span>
</Form.Label>
<Form.Message className="field-invalid" match="valueMissing">
Please enter your date of birth
</Form.Message>
</div>
<Form.Control asChild>
<input
type="date"
className="primary-input"
required
max={new Date().toISOString().split("T")[0]}
/>
</Form.Control>
</Form.Field> */}
</div>
<div className="float-right">
<Form.Submit asChild>
<Button className="mr-3 mt-8">{confirmationText}</Button>
</Form.Submit>
<Button
variant="outline"
onClick={() => {
setOpen(false);
}}
>
{cancelText}
</Button>
</div>
</Form.Root>
</BaseModal.Content>
</BaseModal>
);
}

View file

@ -9,6 +9,7 @@ import {
DialogTitle,
DialogTrigger,
} from "../../components/ui/dialog";
import { modalHeaderType } from "../../types/components";
type ContentProps = { children: ReactNode };
type HeaderProps = { children: ReactNode; description: string };
@ -22,10 +23,10 @@ const Trigger: React.FC<ContentProps> = ({ children }) => {
return <>{children}</>;
};
const Header: React.FC<{ children: ReactNode; description: string }> = ({
const Header: React.FC<{ children: ReactNode; description: string | null }> = ({
children,
description,
}) => {
}: modalHeaderType): JSX.Element => {
return (
<DialogHeader>
<DialogTitle className="flex items-center">{children}</DialogTitle>
@ -47,7 +48,15 @@ interface BaseModalProps {
open?: boolean;
setOpen?: (open: boolean) => void;
disable?: boolean;
size?: "smaller" | "small" | "medium" | "large" | "large-h-full";
size?:
| "x-small"
| "smaller"
| "small"
| "medium"
| "large"
| "large-h-full"
| "small-h-full"
| "medium-h-full";
}
function BaseModal({
open,
@ -73,6 +82,10 @@ function BaseModal({
let height: string;
switch (size) {
case "x-small":
minWidth = "min-w-[20vw]";
height = "h-[10vh]";
break;
case "smaller":
minWidth = "min-w-[40vw]";
height = "h-[27vh]";
@ -81,10 +94,16 @@ function BaseModal({
minWidth = "min-w-[40vw]";
height = "h-[40vh]";
break;
case "small-h-full":
minWidth = "min-w-[40vw]";
break;
case "medium":
minWidth = "min-w-[60vw]";
height = "h-[60vh]";
break;
case "medium-h-full":
minWidth = "min-w-[60vw]";
break;
case "large":
minWidth = "min-w-[80vw]";
height = "h-[80vh]";
@ -108,7 +127,9 @@ function BaseModal({
{triggerChild}
</DialogTrigger>
<DialogContent className={minWidth}>
{headerChild}
<div className="truncate-doubleline word-break-break-word">
{headerChild}
</div>
<div className={`mt-2 flex flex-col ${height} w-full `}>
{ContentChild}
</div>

View file

@ -4,7 +4,7 @@ import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
// import "ace-builds/webpack-resolver";
import { ReactNode, useContext, useEffect, useState } from "react";
import { useContext, useEffect, useState } from "react";
import AceEditor from "react-ace";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
@ -13,7 +13,7 @@ import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext";
import { typesContext } from "../../contexts/typesContext";
import { postCustomComponent, postValidateCode } from "../../controllers/API";
import { APIClassType } from "../../types/api";
import { codeAreaModalPropsType } from "../../types/components";
import BaseModal from "../baseModal";
export default function CodeAreaModal({
@ -23,27 +23,20 @@ export default function CodeAreaModal({
setNodeClass,
children,
dynamic,
}: {
setValue: (value: string) => void;
value: string;
nodeClass?: APIClassType;
children: ReactNode;
setNodeClass?: (Class: APIClassType) => void;
dynamic?: boolean;
}) {
}: codeAreaModalPropsType): JSX.Element {
const [code, setCode] = useState(value);
const { dark } = useContext(darkContext);
const { reactFlowInstance } = useContext(typesContext);
const [height, setHeight] = useState(null);
const [height, setHeight] = useState<string | null>(null);
const { setErrorData, setSuccessData } = useContext(alertContext);
const [error, setError] = useState<{
detail: { error: string; traceback: string };
}>(null);
detail: { error: string | undefined; traceback: string | undefined };
} | null>(null);
useEffect(() => {
// if nodeClass.template has more fields other than code and dynamic is true
// do not run handleClick
if (dynamic && Object.keys(nodeClass.template).length > 2) {
if (dynamic && Object.keys(nodeClass!.template).length > 2) {
return;
}
processCode();
@ -90,7 +83,7 @@ export default function CodeAreaModal({
}
function processDynamicField() {
postCustomComponent(code, nodeClass)
postCustomComponent(code, nodeClass!)
.then((apiReturn) => {
const { data } = apiReturn;
if (data) {

View file

@ -8,77 +8,78 @@ import { TabsContext } from "../../contexts/tabsContext";
import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal";
const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
useContext(TabsContext);
const [checked, setChecked] = useState(false);
const [name, setName] = useState(
flows.find((flow) => flow.id === tabId).name
);
const [invalidName, setInvalidName] = useState(false);
const [description, setDescription] = useState(
flows.find((flow) => flow.id === tabId).description
);
const [open, setOpen] = useState(false);
return (
<BaseModal size="smaller" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
<BaseModal.Header description={EXPORT_DIALOG_SUBTITLE}>
<span className="pr-2">Export</span>
<IconComponent
name="Download"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<EditFlowSettings
invalidName={invalidName}
setInvalidName={setInvalidName}
name={name}
description={description}
flows={flows}
tabId={tabId}
setName={setName}
setDescription={setDescription}
updateFlow={updateFlow}
/>
<div className="mt-3 flex items-center space-x-2">
<Checkbox
id="terms"
onCheckedChange={(event: boolean) => {
setChecked(event);
}}
const ExportModal = forwardRef(
(props: { children: ReactNode }, ref): JSX.Element => {
const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
useContext(TabsContext);
const [checked, setChecked] = useState(false);
const [name, setName] = useState(
flows.find((flow) => flow.id === tabId)?.name
);
const [invalidName, setInvalidName] = useState(false);
const [description, setDescription] = useState(
flows.find((flow) => flow.id === tabId)?.description
);
const [open, setOpen] = useState(false);
return (
<BaseModal size="smaller" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{props.children}</BaseModal.Trigger>
<BaseModal.Header description={EXPORT_DIALOG_SUBTITLE}>
<span className="pr-2">Export</span>
<IconComponent
name="Download"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>
<label htmlFor="terms" className="export-modal-save-api text-sm ">
Save with my API keys
</label>
</div>
</BaseModal.Content>
</BaseModal.Header>
<BaseModal.Content>
<EditFlowSettings
invalidName={invalidName}
setInvalidName={setInvalidName}
name={name!}
description={description!}
flows={flows}
tabId={tabId}
setName={setName}
setDescription={setDescription}
/>
<div className="mt-3 flex items-center space-x-2">
<Checkbox
id="terms"
onCheckedChange={(event: boolean): void => {
setChecked(event);
}}
/>
<label htmlFor="terms" className="export-modal-save-api text-sm ">
Save with my API keys
</label>
</div>
</BaseModal.Content>
<BaseModal.Footer>
<Button
onClick={() => {
if (checked)
downloadFlow(
flows.find((flow) => flow.id === tabId),
name,
description
);
else
downloadFlow(
removeApiKeys(flows.find((flow) => flow.id === tabId)),
name,
description
);
setOpen(false);
}}
type="submit"
>
Download Flow
</Button>
</BaseModal.Footer>
</BaseModal>
);
});
<BaseModal.Footer>
<Button
onClick={() => {
if (checked)
downloadFlow(
flows.find((flow) => flow.id === tabId)!,
name!,
description
);
else
downloadFlow(
removeApiKeys(flows.find((flow) => flow.id === tabId)!),
name!,
description
);
setOpen(false);
}}
type="submit"
>
Download Flow
</Button>
</BaseModal.Footer>
</BaseModal>
);
}
);
export default ExportModal;

View file

@ -5,33 +5,30 @@ import { Button } from "../../components/ui/button";
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { TabsContext } from "../../contexts/tabsContext";
import { FlowSettingsPropsType } from "../../types/components";
import BaseModal from "../baseModal";
export default function FlowSettingsModal({
open,
setOpen,
}: {
open: boolean;
setOpen: (open: boolean) => void;
}) {
}: FlowSettingsPropsType): JSX.Element {
const { setErrorData, setSuccessData } = useContext(alertContext);
const ref = useRef();
const { flows, tabId, updateFlow, setTabsState, saveFlow } =
useContext(TabsContext);
const { flows, tabId, updateFlow, saveFlow } = useContext(TabsContext);
const maxLength = 50;
const [name, setName] = useState(
flows.find((flow) => flow.id === tabId).name
flows.find((flow) => flow.id === tabId)!.name
);
const [description, setDescription] = useState(
flows.find((flow) => flow.id === tabId).description
flows.find((flow) => flow.id === tabId)!.description
);
const [invalidName, setInvalidName] = useState(false);
function handleClick() {
function handleClick(): void {
let savedFlow = flows.find((flow) => flow.id === tabId);
savedFlow.name = name;
savedFlow.description = description;
saveFlow(savedFlow);
savedFlow!.name = name;
savedFlow!.description = description;
saveFlow(savedFlow!);
setSuccessData({ title: "Changes saved successfully" });
setOpen(false);
}
@ -51,7 +48,6 @@ export default function FlowSettingsModal({
tabId={tabId}
setName={setName}
setDescription={setDescription}
updateFlow={updateFlow}
/>
</BaseModal.Content>

View file

@ -1,6 +1,7 @@
import { useEffect } from "react";
import IconComponent from "../../../components/genericIconComponent";
import { Textarea } from "../../../components/ui/textarea";
import { chatInputType } from "../../../types/components";
import { classNames } from "../../../utils/utils";
export default function ChatInput({
@ -10,7 +11,7 @@ export default function ChatInput({
setChatValue,
inputRef,
noInput,
}) {
}: chatInputType): JSX.Element {
useEffect(() => {
if (!lockChat && inputRef.current) {
inputRef.current.focus();
@ -46,7 +47,7 @@ export default function ChatInput({
}`,
}}
value={lockChat ? "Thinking..." : chatValue}
onChange={(event) => {
onChange={(event): void => {
setChatValue(event.target.value);
}}
className={classNames(
@ -75,7 +76,7 @@ export default function ChatInput({
: "bg-chat-send text-background"
)}
disabled={lockChat}
onClick={() => sendMessage()}
onClick={(): void => sendMessage()}
>
{lockChat ? (
<IconComponent

View file

@ -3,16 +3,12 @@ import { useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { programmingLanguages } from "../../../../constants/constants";
import { Props } from "../../../../types/components";
interface Props {
language: string;
value: string;
}
export function CodeBlock({ language, value }) {
export function CodeBlock({ language, value }: Props): JSX.Element {
const [isCopied, setIsCopied] = useState<Boolean>(false);
const copyToClipboard = () => {
const copyToClipboard = (): void => {
if (!navigator.clipboard || !navigator.clipboard.writeText) {
return;
}
@ -25,7 +21,7 @@ export function CodeBlock({ language, value }) {
}, 2000);
});
};
const downloadAsFile = () => {
const downloadAsFile = (): void => {
const fileExtension = programmingLanguages[language] || ".file";
const suggestedFileName = `${"generated-code"}${fileExtension}`;
const fileName = window.prompt("enter file name", suggestedFileName);

View file

@ -9,18 +9,15 @@ import Robot from "../../../assets/robot.png";
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
import CodeTabsComponent from "../../../components/codeTabsComponent";
import IconComponent from "../../../components/genericIconComponent";
import { ChatMessageType } from "../../../types/chat";
import { chatMessagePropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils";
import FileCard from "../fileComponent";
export default function ChatMessage({
chat,
lockChat,
lastMessage,
}: {
chat: ChatMessageType;
lockChat: boolean;
lastMessage: boolean;
}) {
}: chatMessagePropsType): JSX.Element {
const convert = new Convert({ newline: true });
const [hidden, setHidden] = useState(true);
const template = chat.template;
@ -57,7 +54,7 @@ export default function ChatMessage({
<div className="form-modal-chat-text">
{hidden && chat.thought && chat.thought !== "" && (
<div
onClick={() => setHidden((prev) => !prev)}
onClick={(): void => setHidden((prev) => !prev)}
className="form-modal-chat-icon-div"
>
<IconComponent
@ -192,7 +189,7 @@ export default function ChatMessage({
? template?.split("\n")?.map((line, index) => {
const regex = /{([^}]+)}/g;
let match;
let parts = [];
let parts: Array<JSX.Element | string> = [];
let lastIndex = 0;
while ((match = regex.exec(line)) !== null) {
// Push text up to the match

View file

@ -1,9 +1,14 @@
import * as base64js from "base64-js";
import { useState } from "react";
import IconComponent from "../../../components/genericIconComponent";
import { fileCardPropsType } from "../../../types/components";
export default function FileCard({ fileName, content, fileType }) {
const handleDownload = () => {
export default function FileCard({
fileName,
content,
fileType,
}: fileCardPropsType): JSX.Element {
const handleDownload = (): void => {
const byteArray = new Uint8Array(base64js.toByteArray(content));
const blob = new Blob([byteArray], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
@ -16,10 +21,10 @@ export default function FileCard({ fileName, content, fileType }) {
URL.revokeObjectURL(url);
};
const [isHovered, setIsHovered] = useState(false);
function handleMouseEnter() {
function handleMouseEnter(): void {
setIsHovered(true);
}
function handleMouseLeave() {
function handleMouseLeave(): void {
setIsHovered(false);
}
@ -55,7 +60,7 @@ export default function FileCard({ fileName, content, fileType }) {
return (
<button onClick={handleDownload} className="file-card-modal-button">
<div className="file-card-modal-div">
{" "}
ooooooooooooooo{" "}
{fileType === "image" ? (
<img
src={`data:image/png;base64,${content}`}

View file

@ -24,6 +24,7 @@ import {
import { Textarea } from "../../components/ui/textarea";
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext";
import { TabsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils";
export default function FormModal({
@ -34,7 +35,7 @@ export default function FormModal({
open: boolean;
setOpen: (open: boolean) => void;
flow: FlowType;
}) {
}): JSX.Element {
const { tabsState, setTabsState } = useContext(TabsContext);
const [chatValue, setChatValue] = useState(() => {
try {
@ -45,11 +46,11 @@ export default function FormModal({
const inputKeys = formKeysData.input_keys;
const handleKeys = formKeysData.handle_keys;
const keyToUse = Object.keys(inputKeys).find(
(key) => !handleKeys.some((j) => j === key) && inputKeys[key] === ""
const keyToUse = Object.keys(inputKeys!).find(
(key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === ""
);
return inputKeys[keyToUse];
return inputKeys![keyToUse!];
} catch (error) {
console.error(error);
// return a sensible default or `undefined` if no default is possible
@ -63,16 +64,17 @@ export default function FormModal({
const ws = useRef<WebSocket | null>(null);
const [lockChat, setLockChat] = useState(false);
const isOpen = useRef(open);
const messagesRef = useRef(null);
const messagesRef = useRef<HTMLDivElement | null>(null);
const id = useRef(flow.id);
const tabsStateFlowId = tabsState[flow.id];
const tabsStateFlowIdFormKeysData = tabsStateFlowId.formKeysData;
const [chatKey, setChatKey] = useState(() => {
if (tabsState[flow.id]?.formKeysData?.input_keys) {
return Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
return Object.keys(tabsState[flow.id].formKeysData.input_keys!).find(
(key) =>
!tabsState[flow.id].formKeysData.handle_keys.some((j) => j === key) &&
tabsState[flow.id].formKeysData.input_keys[key] === ""
!tabsState[flow.id].formKeysData.handle_keys!.some(
(j) => j === key
) && tabsState[flow.id].formKeysData.input_keys![key] === ""
);
}
// TODO: return a sensible default
@ -149,7 +151,7 @@ export default function FormModal({
});
}
function handleOnClose(event: CloseEvent) {
function handleOnClose(event: CloseEvent): void {
if (isOpen.current) {
setErrorData({ title: event.reason });
setTimeout(() => {
@ -159,7 +161,10 @@ export default function FormModal({
}
}
function getWebSocketUrl(chatId, isDevelopment = false) {
function getWebSocketUrl(
chatId: string,
isDevelopment: boolean = false
): string {
const isSecureProtocol =
window.location.protocol === "https:" || window.location.port === "443";
const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
@ -212,7 +217,7 @@ export default function FormModal({
});
}
if (data.type === "start") {
addChatHistory("", false, chatKey);
addChatHistory("", false, chatKey!);
isStream = true;
}
if (data.type === "end") {
@ -241,7 +246,7 @@ export default function FormModal({
}
}
function connectWS() {
function connectWS(): void {
try {
const urlWs = getWebSocketUrl(
id.current,
@ -308,15 +313,15 @@ export default function FormModal({
// do not add connectWS on dependencies array
}, [lockChat]);
async function sendAll(data: sendAllProps) {
async function sendAll(data: sendAllProps): Promise<void> {
try {
if (ws) {
ws.current.send(JSON.stringify(data));
ws.current?.send(JSON.stringify(data));
}
} catch (error) {
setErrorData({
title: "There was an error sending the message",
list: [error.message],
list: [(error as { message: string }).message],
});
setChatValue(data.inputs);
connectWS();
@ -327,7 +332,7 @@ export default function FormModal({
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
}, [chatHistory]);
const ref = useRef(null);
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (open && ref.current) {
@ -335,30 +340,31 @@ export default function FormModal({
}
}, [open]);
function sendMessage() {
let nodeValidationErrors = validateNodes(reactFlowInstance);
function sendMessage(): void {
let nodeValidationErrors = validateNodes(reactFlowInstance!);
if (nodeValidationErrors.length === 0) {
setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys;
setChatValue("");
const message = inputs;
addChatHistory(
message,
message!,
true,
chatKey,
chatKey!,
tabsState[flow.id].formKeysData.template
);
sendAll({
...reactFlowInstance.toObject(),
inputs: inputs,
...reactFlowInstance?.toObject()!,
inputs: inputs!,
chatHistory,
name: flow.name,
description: flow.description,
});
setTabsState((old) => {
//@ts-ignore
setTabsState((old: TabsState) => {
if (!chatKey) return old;
let newTabsState = _.cloneDeep(old);
newTabsState[id.current].formKeysData.input_keys[chatKey] = "";
newTabsState[id.current].formKeysData.input_keys![chatKey] = "";
return newTabsState;
});
} else {
@ -368,18 +374,18 @@ export default function FormModal({
});
}
}
function clearChat() {
function clearChat(): void {
setChatHistory([]);
ws.current.send(JSON.stringify({ clear_history: true }));
ws.current?.send(JSON.stringify({ clear_history: true }));
if (lockChat) setLockChat(false);
}
function handleOnCheckedChange(checked: boolean, i: string) {
if (checked === true) {
setChatKey(i);
setChatValue(tabsState[flow.id].formKeysData.input_keys[i]);
setChatValue(tabsState[flow.id].formKeysData.input_keys![i]);
} else {
setChatKey(null);
setChatKey(null!);
setChatValue("");
}
}
@ -425,7 +431,7 @@ export default function FormModal({
{tabsState[id.current]?.formKeysData?.input_keys
? Object.keys(
tabsState[id.current].formKeysData.input_keys
tabsState[id.current].formKeysData.input_keys!
).map((key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
@ -449,7 +455,7 @@ export default function FormModal({
size="small"
disabled={tabsState[
id.current
].formKeysData.handle_keys.some(
].formKeysData.handle_keys!.some(
(t) => t === key
)}
/>
@ -460,7 +466,7 @@ export default function FormModal({
keyValue={key}
>
<div className="file-component-tab-column">
{tabsState[id.current].formKeysData.handle_keys.some(
{tabsState[id.current].formKeysData.handle_keys!.some(
(t) => t === key
) && (
<div className="font-normal text-muted-foreground ">
@ -470,14 +476,18 @@ export default function FormModal({
<Textarea
className="custom-scroll"
value={
tabsState[id.current].formKeysData.input_keys[key]
tabsState[id.current].formKeysData.input_keys![
key
]
}
onChange={(e) => {
setTabsState((old) => {
//@ts-ignore
setTabsState((old: TabsState) => {
let newTabsState = _.cloneDeep(old);
newTabsState[
id.current
].formKeysData.input_keys[key] = e.target.value;
].formKeysData.input_keys![key] =
e.target.value;
return newTabsState;
});
}}
@ -489,7 +499,7 @@ export default function FormModal({
</div>
))
: null}
{tabsState[id.current].formKeysData.memory_keys.map(
{tabsState[id.current].formKeysData.memory_keys!.map(
(key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
@ -583,10 +593,11 @@ export default function FormModal({
sendMessage={sendMessage}
setChatValue={(value) => {
setChatValue(value);
setTabsState((old) => {
//@ts-ignore
setTabsState((old: TabsState) => {
let newTabsState = _.cloneDeep(old);
newTabsState[id.current].formKeysData.input_keys[
chatKey
newTabsState[id.current].formKeysData.input_keys![
chatKey!
] = value;
return newTabsState;
});

View file

@ -1,4 +1,4 @@
import { ReactNode, useContext, useEffect, useRef, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent";
@ -15,7 +15,7 @@ import {
import { TypeModal } from "../../constants/enums";
import { alertContext } from "../../contexts/alertContext";
import { postValidatePrompt } from "../../controllers/API";
import { APIClassType } from "../../types/api";
import { genericModalPropsType } from "../../types/components";
import {
classNames,
getRandomKeyByssmm,
@ -33,39 +33,29 @@ export default function GenericModal({
nodeClass,
setNodeClass,
children,
}: {
field_name?: string;
setValue: (value: string) => void;
value: string;
buttonText: string;
modalTitle: string;
type: number;
children: ReactNode;
nodeClass?: APIClassType;
setNodeClass?: (Class: APIClassType) => void;
}) {
}: genericModalPropsType): JSX.Element {
const [myButtonText] = useState(buttonText);
const [myModalTitle] = useState(modalTitle);
const [myModalType] = useState(type);
const [inputValue, setInputValue] = useState(value);
const [isEdit, setIsEdit] = useState(true);
const [wordsHighlight, setWordsHighlight] = useState([]);
const [wordsHighlight, setWordsHighlight] = useState<string[]>([]);
const { setErrorData, setSuccessData, setNoticeData } =
useContext(alertContext);
const ref = useRef();
const divRef = useRef(null);
const divRefPrompt = useRef(null);
function checkVariables(valueToCheck) {
function checkVariables(valueToCheck: string): void {
const regex = /\{([^{}]+)\}/g;
const matches = [];
const matches: string[] = [];
let match;
while ((match = regex.exec(valueToCheck))) {
matches.push(`{${match[1]}}`);
}
let invalid_chars = [];
let fixed_variables = [];
let invalid_chars: string[] = [];
let fixed_variables: string[] = [];
let input_variables = matches;
for (let variable of input_variables) {
let new_var = variable;
@ -106,7 +96,7 @@ export default function GenericModal({
.replace(regexHighlight, varHighlightHTML({ name: "$1" }))
.replace(/\n/g, "<br />");
const TextAreaContentView = () => {
const TextAreaContentView = (): JSX.Element => {
return (
<SanitizedHTMLWrapper
className={getClassByNumberLength()}
@ -119,7 +109,7 @@ export default function GenericModal({
);
};
function getClassByNumberLength() {
function getClassByNumberLength(): string {
let sumOfCaracteres: number = 0;
wordsHighlight.forEach((element) => {
sumOfCaracteres = sumOfCaracteres + element.replace(/[{}]/g, "").length;
@ -129,8 +119,8 @@ export default function GenericModal({
: "code-nohighlight";
}
function validatePrompt(closeModal: boolean) {
postValidatePrompt(field_name, inputValue, nodeClass)
function validatePrompt(closeModal: boolean): void {
postValidatePrompt(field_name, inputValue, nodeClass!)
.then((apiReturn) => {
if (apiReturn.data) {
let inputVariables = apiReturn.data.input_variables ?? [];
@ -144,7 +134,7 @@ export default function GenericModal({
setSuccessData({
title: "Prompt is ready",
});
setNodeClass(apiReturn.data?.frontend_node);
setNodeClass!(apiReturn.data?.frontend_node);
setModalOpen(closeModal);
setValue(inputValue);
}
@ -218,6 +208,7 @@ export default function GenericModal({
<TextAreaContentView />
) : type !== TypeModal.PROMPT ? (
<Textarea
//@ts-ignore
ref={ref}
className="form-input h-full w-full rounded-lg focus-visible:ring-1"
value={inputValue}

View file

@ -1,4 +1,4 @@
import { ReactNode } from "react";
import { buttonBoxPropsType } from "../../../types/components";
import { classNames } from "../../../utils/utils";
export default function ButtonBox({
@ -10,16 +10,7 @@ export default function ButtonBox({
textColor,
deactivate,
size,
}: {
onClick: () => void;
title: string;
description: string;
icon: ReactNode;
bgColor: string;
textColor: string;
deactivate?: boolean;
size: "small" | "medium" | "big";
}) {
}: buttonBoxPropsType): JSX.Element {
let bigCircle: string;
let smallCircle: string;
let titleFontSize: string;

View file

@ -23,7 +23,7 @@ import { FlowType } from "../../types/flow";
import { classNames } from "../../utils/utils";
import ButtonBox from "./buttonBox";
export default function ImportModal() {
export default function ImportModal(): JSX.Element {
const [open, setOpen] = useState(true);
const { setErrorData } = useContext(alertContext);
const ref = useRef();
@ -32,7 +32,7 @@ export default function ImportModal() {
const [examples, setExamples] = useState<FlowType[]>([]);
const { uploadFlow, addFlow } = useContext(TabsContext);
function handleExamples() {
function handleExamples(): void {
setLoadingExamples(true);
getExamples()
.then((result) => {

View file

@ -0,0 +1,31 @@
import { useNavigate } from "react-router-dom";
import { Button } from "../../../components/ui/button";
import { Input } from "../../../components/ui/input";
export default function LoginAdminPage() {
const navigate = useNavigate();
function loginAdmin() {
navigate("/admin/");
}
return (
<div className="flex h-full w-full flex-col items-center justify-center bg-muted">
<div className="flex w-72 flex-col items-center justify-center gap-2">
<span className="mb-4 text-5xl"></span>
<span className="mb-6 text-2xl font-semibold text-primary">Admin</span>
<Input className="bg-background" placeholder="Email address" />
<Input className="bg-background" placeholder="Password" />
<Button
onClick={() => {
loginAdmin();
}}
variant="default"
className="w-full"
>
Login
</Button>
</div>
</div>
);
}

View file

@ -0,0 +1,401 @@
import _ from "lodash";
import { X } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import ConfirmationModal from "../../modals/ConfirmationModal";
import UserManagementModal from "../../modals/UserManagementModal";
export default function AdminPage() {
const [inputValue, setInputValue] = useState("");
const [size, setPageSize] = useState(10);
const [index, setPageIndex] = useState(1);
const userList = useRef([
{
user: generateRandomString(50),
email: generateRandomString(50) + "@example.com",
password: generateRandomString(50),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
{
user: generateRandomString(8),
email: generateRandomString(10) + "@example.com",
password: generateRandomString(12),
register_date: generateRandomDate(),
},
]);
const [filterUserList, setFilterUserList] = useState(userList.current);
function generateRandomString(length) {
const characters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let result = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters.charAt(randomIndex);
}
return result;
}
function generateRandomDate() {
const start = new Date(2010, 0, 1);
const end = new Date();
const randomTimestamp =
start.getTime() + Math.random() * (end.getTime() - start.getTime());
const randomDate = new Date(randomTimestamp);
const options = { year: "numeric", month: "short", day: "numeric" };
return randomDate.toLocaleDateString("en-US");
}
const [editUser, setEditUser] = useState(-1);
const [editedUser, setEditedUser] = useState("");
const [modalEditOpen, setModalEditOpen] = useState(false);
const [modalDeleteOpen, setModalDeleteOpen] = useState(false);
useEffect(() => {
resetFilter();
}, []);
const handleInputChange = (event, index) => {
const user = _.cloneDeepWith(userList.current);
user[index].password = event.target.value;
userList.current = user;
const userFilter = _.cloneDeepWith(filterUserList);
userFilter[index].password = event.target.value;
setFilterUserList(userFilter);
setEditedUser(event.target.value);
};
function handleChangePagination(pageIndex: number, pageSize: number) {
setPageIndex(pageIndex);
setPageSize(pageSize);
const startIndex = (pageIndex - 1) * pageSize;
const endIndex = startIndex + pageSize;
const newList = userList.current.slice(startIndex, endIndex);
setFilterUserList(newList);
}
function resetFilter() {
setFilterUserList(userList.current);
setPageIndex(1);
setPageSize(10);
const startIndex = (index - 1) * size;
const endIndex = index + size - 1;
const newList = userList.current.slice(startIndex, endIndex);
console.log(userList.current);
setFilterUserList(newList);
}
function handleFilterUsers(input: string) {
setInputValue(input);
if (input === "") {
resetFilter();
} else {
const filteredList = userList.current.filter(
(user) =>
user.user.toLowerCase().includes(input.toLowerCase()) ||
user.email.toLowerCase().includes(input.toLowerCase())
);
setFilterUserList(filteredList);
}
}
function handleDeleteUser(index) {
const user = _.cloneDeepWith(userList.current);
user.splice(index, 1);
userList.current = user;
const userFilter = _.cloneDeepWith(filterUserList);
userFilter.splice(index, 1);
setFilterUserList(userFilter);
resetFilter();
}
function handleEditUser(index, user) {
const newUser = _.cloneDeepWith(userList.current);
newUser[index].password = user.password;
newUser[index].user = user.username;
userList.current = newUser;
resetFilter();
}
function handleNewUser(user) {
const newUser = {
user: user.username,
email: generateRandomString(50) + "@example.com",
password: user.password,
register_date: generateRandomDate(),
};
userList.current.unshift(newUser);
console.log(userList.current);
resetFilter();
}
return (
<>
<div className="main-page-panel">
<div className="m-auto flex h-full flex-row justify-center">
<div className="basis-5/6">
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
<div className="flex items-center justify-between space-y-2">
<div>
<h2 className="text-2xl font-bold tracking-tight">
Welcome back!
</h2>
<p className="text-muted-foreground">
Here&apos;s a list of all users!
</p>
</div>
<div className="flex items-center space-x-2"></div>
</div>
{userList.current.length === 0 && (
<>
<div className="flex items-center justify-between">
<h2>There's no users left :)</h2>
</div>
</>
)}
{userList.current.length > 0 && (
<>
<div className="flex items-center justify-between">
<div className="flex flex-1 items-center space-x-2">
<Input
value={inputValue}
placeholder="Filter users..."
className="h-8 w-[150px] lg:w-[250px]"
onChange={(e) => handleFilterUsers(e.target.value)}
/>
{inputValue.length > 0 && (
<Button
onClick={() => {
resetFilter();
setInputValue("");
}}
variant="ghost"
className="h-8 px-2 lg:px-3"
>
Reset
<X className="ml-2 h-4 w-4" />
</Button>
)}
</div>
<div>
<UserManagementModal
title="New User"
titleHeader={"Add a new user"}
cancelText="Cancel"
confirmationText="Save"
icon={"UserPlus2"}
onConfirm={(index, user) => {
handleNewUser(user);
}}
>
<Button>New User</Button>
</UserManagementModal>
</div>
</div>
<div
className="overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted
custom-scroll"
>
<Table className="table-fixed bg-muted outline-1 ">
<TableHeader>
<TableRow>
<TableHead className="h-10">User</TableHead>
<TableHead className="h-10">Password</TableHead>
<TableHead className="h-10 w-[100px] text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filterUserList.map((user, index) => (
<TableRow key={user.user}>
<TableCell className="truncate py-2 font-medium">
{user.user}
</TableCell>
<TableCell className="truncate py-2">
{user.password}
</TableCell>
<TableCell className="flex w-[100px] py-2 text-right">
<div className="flex">
<UserManagementModal
title="Edit"
titleHeader={`${user.user}`}
cancelText="Cancel"
confirmationText="Edit"
icon={"UserPlus2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleEditUser(index, user);
}}
>
<ShadTooltip content="Edit" side="top">
<IconComponent
name="Pencil"
className="h-4 w-4 cursor-pointer"
/>
</ShadTooltip>
</UserManagementModal>
<ConfirmationModal
title="Delete"
titleHeader="Delete User"
modalContentTitle="Attention!"
modalContent="Are you sure you want to delete this user? This action cannot be undone."
cancelText="Cancel"
confirmationText="Delete"
icon={"UserMinus2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleDeleteUser(index);
}}
>
<ShadTooltip content="Delete" side="top">
<IconComponent
name="Trash2"
className="ml-2 h-4 w-4 cursor-pointer"
/>
</ShadTooltip>
</ConfirmationModal>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<PaginatorComponent
pageIndex={index}
pageSize={size}
totalRowsCount={filterUserList.length}
paginate={(pageIndex, pageSize) => {
handleChangePagination(pageSize, pageIndex);
}}
></PaginatorComponent>
</>
)}
</div>
</div>
</div>
</div>
</>
);
}

View file

@ -8,7 +8,7 @@ import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent";
import { getExamples } from "../../controllers/API";
import { FlowType } from "../../types/flow";
export default function CommunityPage() {
export default function CommunityPage(): JSX.Element {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow } =
useContext(TabsContext);
@ -21,7 +21,7 @@ export default function CommunityPage() {
const [examples, setExamples] = useState<FlowType[]>([]);
// Show community examples on screen
function handleExamples() {
function handleExamples(): void {
setLoadingExamples(true);
getExamples()
.then((result) => {

View file

@ -6,7 +6,7 @@ const ConnectionLineComponent = ({
toX,
toY,
connectionLineStyle = {}, // provide a default value for connectionLineStyle
}: ConnectionLineComponentProps) => {
}: ConnectionLineComponentProps): JSX.Element => {
return (
<g>
<path

View file

@ -6,7 +6,7 @@ export default function DisclosureComponent({
button: { title, Icon, buttons = [] },
children,
openDisc,
}: DisclosureComponentType) {
}: DisclosureComponentType): JSX.Element {
return (
<Disclosure as="div" key={title}>
{({ open }) => (

View file

@ -1,11 +1,19 @@
import _ from "lodash";
import { useCallback, useContext, useEffect, useRef, useState } from "react";
import {
MouseEvent,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import ReactFlow, {
Background,
Connection,
Controls,
Edge,
EdgeChange,
Node,
NodeChange,
NodeDragHandler,
OnEdgesDelete,
@ -26,6 +34,7 @@ import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType } from "../../../../types/flow";
import { TabsState } from "../../../../types/tabs";
import { isValidConnection } from "../../../../utils/reactflowUtils";
import { isWrappedWithClass } from "../../../../utils/utils";
import ConnectionLineComponent from "../ConnectionLineComponent";
@ -35,7 +44,7 @@ const nodeTypes = {
genericNode: GenericNode,
};
export default function Page({ flow }: { flow: FlowType }) {
export default function Page({ flow }: { flow: FlowType }): JSX.Element {
let {
updateFlow,
uploadFlow,
@ -51,13 +60,13 @@ export default function Page({ flow }: { flow: FlowType }) {
} = useContext(TabsContext);
const { types, reactFlowInstance, setReactFlowInstance, templates } =
useContext(typesContext);
const reactFlowWrapper = useRef(null);
const reactFlowWrapper = useRef<HTMLDivElement>(null);
const { takeSnapshot } = useContext(undoRedoContext);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [lastSelection, setLastSelection] =
useState<OnSelectionChangeParams>(null);
useState<OnSelectionChangeParams | null>(null);
useEffect(() => {
// this effect is used to attach the global event handlers
@ -78,10 +87,10 @@ export default function Page({ flow }: { flow: FlowType }) {
lastCopiedSelection
) {
event.preventDefault();
let bounds = reactFlowWrapper.current.getBoundingClientRect();
let bounds = reactFlowWrapper.current?.getBoundingClientRect();
paste(lastCopiedSelection, {
x: position.x - bounds.left,
y: position.y - bounds.top,
x: position.x - bounds!.left,
y: position.y - bounds!.top,
});
}
if (
@ -147,7 +156,8 @@ export default function Page({ flow }: { flow: FlowType }) {
let newX = _.cloneDeep(node);
return newX;
});
setTabsState((prev) => {
//@ts-ignore
setTabsState((prev: TabsState) => {
return {
...prev,
[tabId]: {
@ -163,7 +173,8 @@ export default function Page({ flow }: { flow: FlowType }) {
const onNodesChangeMod = useCallback(
(change: NodeChange[]) => {
onNodesChange(change);
setTabsState((prev) => {
//@ts-ignore
setTabsState((prev: TabsState) => {
return {
...prev,
[tabId]: {
@ -185,10 +196,10 @@ export default function Page({ flow }: { flow: FlowType }) {
...params,
style: { stroke: "#555" },
className:
(params.targetHandle.split("|")[0] === "Text"
(params.targetHandle?.split("|")[0] === "Text"
? "stroke-foreground "
: "stroke-foreground ") + " stroke-connection",
animated: params.targetHandle.split("|")[0] === "Text",
animated: params.targetHandle?.split("|")[0] === "Text",
},
eds
)
@ -234,7 +245,7 @@ export default function Page({ flow }: { flow: FlowType }) {
// Get the current bounds of the ReactFlow wrapper element
const reactflowBounds =
reactFlowWrapper.current.getBoundingClientRect();
reactFlowWrapper.current?.getBoundingClientRect();
// Extract the data from the drag event and parse it as a JSON object
let data: { type: string; node?: APIClassType } = JSON.parse(
@ -243,9 +254,9 @@ export default function Page({ flow }: { flow: FlowType }) {
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
// Calculate the position where the node should be created
const position = reactFlowInstance.project({
x: event.clientX - reactflowBounds.left,
y: event.clientY - reactflowBounds.top,
const position = reactFlowInstance!.project({
x: event.clientX - reactflowBounds!.left,
y: event.clientY - reactflowBounds!.top,
});
// Generate a unique node ID
@ -262,7 +273,6 @@ export default function Page({ flow }: { flow: FlowType }) {
data: {
...data,
id: newId,
value: null,
},
};
} else {
@ -274,7 +284,6 @@ export default function Page({ flow }: { flow: FlowType }) {
data: {
...data,
id: newId,
value: null,
},
};
@ -283,7 +292,7 @@ export default function Page({ flow }: { flow: FlowType }) {
setNodes((nds) => nds.concat(newNode));
} else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot();
uploadFlow(false, event.dataTransfer.files.item(0));
uploadFlow(false, event.dataTransfer.files.item(0)!);
}
},
// Specify dependencies for useCallback
@ -299,7 +308,7 @@ export default function Page({ flow }: { flow: FlowType }) {
}, []);
const onDelete = useCallback(
(mynodes) => {
(mynodes: Node[]) => {
takeSnapshot();
setEdges(
edges.filter(
@ -319,7 +328,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onEdgeUpdate = useCallback(
(oldEdge: Edge, newConnection: Connection) => {
if (isValidConnection(newConnection, reactFlowInstance)) {
if (isValidConnection(newConnection, reactFlowInstance!)) {
edgeUpdateSuccessful.current = true;
setEdges((els) => updateEdge(oldEdge, newConnection, els));
}
@ -327,11 +336,10 @@ export default function Page({ flow }: { flow: FlowType }) {
[reactFlowInstance, setEdges]
);
const onEdgeUpdateEnd = useCallback((_, edge) => {
const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => {
if (!edgeUpdateSuccessful.current) {
setEdges((eds) => eds.filter((edg) => edg.id !== edge.id));
}
edgeUpdateSuccessful.current = true;
}, []);
@ -340,7 +348,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onSelectionEnd = useCallback(() => {
setSelectionEnded(true);
}, []);
const onSelectionStart = useCallback((event) => {
const onSelectionStart = useCallback((event: MouseEvent) => {
event.preventDefault();
setSelectionEnded(false);
}, []);
@ -354,9 +362,12 @@ export default function Page({ flow }: { flow: FlowType }) {
}
}, [selectionEnded, lastSelection]);
const onSelectionChange = useCallback((flow) => {
setLastSelection(flow);
}, []);
const onSelectionChange = useCallback(
(flow: OnSelectionChangeParams): void => {
setLastSelection(flow);
},
[]
);
return (
<div className="flex h-full overflow-hidden">
@ -383,7 +394,6 @@ export default function Page({ flow }: { flow: FlowType }) {
onEdgesChange={onEdgesChangeMod}
onConnect={onConnect}
disableKeyboardA11y={true}
onLoad={setReactFlowInstance}
onInit={setReactFlowInstance}
nodeTypes={nodeTypes}
onEdgeUpdate={onEdgeUpdate}
@ -409,7 +419,7 @@ export default function Page({ flow }: { flow: FlowType }) {
[&>button]:border-b-border hover:[&>button]:bg-border"
></Controls>
</ReactFlow>
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
<Chat flow={flow} reactFlowInstance={reactFlowInstance!} />
</div>
) : (
<></>

View file

@ -17,7 +17,7 @@ import {
import { classNames } from "../../../../utils/utils";
import DisclosureComponent from "../DisclosureComponent";
export default function ExtraSidebar() {
export default function ExtraSidebar(): JSX.Element {
const { data, templates } = useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
useContext(TabsContext);
@ -28,7 +28,7 @@ export default function ExtraSidebar() {
function onDragStart(
event: React.DragEvent<any>,
data: { type: string; node?: APIClassType }
) {
): void {
//start drag event
var crt = event.currentTarget.cloneNode(true);
crt.style.position = "absolute";
@ -59,7 +59,7 @@ export default function ExtraSidebar() {
const flow = flows.find((flow) => flow.id === tabId);
useEffect(() => {
// show components with error on load
let errors = [];
let errors: string[] = [];
Object.keys(templates).forEach((component) => {
if (templates[component].error) {
errors.push(component);
@ -120,7 +120,7 @@ export default function ExtraSidebar() {
"extra-side-bar-buttons " + (isPending ? "" : "button-disable")
}
onClick={(event) => {
saveFlow(flow);
saveFlow(flow!);
setSuccessData({ title: "Changes saved successfully" });
}}
>

View file

@ -4,14 +4,19 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { TabsContext } from "../../../../contexts/tabsContext";
import EditNodeModal from "../../../../modals/EditNodeModal";
import { nodeToolbarPropsType } from "../../../../types/components";
import { classNames } from "../../../../utils/utils";
export default function NodeToolbarComponent({ data, setData, deleteNode }) {
export default function NodeToolbarComponent({
data,
setData,
deleteNode,
}: nodeToolbarPropsType): JSX.Element {
const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter(
Object.keys(data.node!.template).filter(
(templateField) =>
templateField.charAt(0) !== "_" &&
data.node.template[templateField].show &&
data.node?.template[templateField].show &&
(data.node.template[templateField].type === "str" ||
data.node.template[templateField].type === "bool" ||
data.node.template[templateField].type === "float" ||
@ -55,8 +60,8 @@ export default function NodeToolbarComponent({ data, setData, deleteNode }) {
{
x: 50,
y: 10,
paneX: reactFlowInstance.getNode(data.id).position.x,
paneY: reactFlowInstance.getNode(data.id).position.y,
paneX: reactFlowInstance.getNode(data.id)?.position.x,
paneY: reactFlowInstance.getNode(data.id)?.position.y,
}
);
}}
@ -67,23 +72,23 @@ export default function NodeToolbarComponent({ data, setData, deleteNode }) {
<ShadTooltip
content={
data.node.documentation === "" ? "Coming Soon" : "Documentation"
data.node?.documentation === "" ? "Coming Soon" : "Documentation"
}
side="top"
>
<a
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
(data.node.documentation === ""
(data.node?.documentation === ""
? " text-muted-foreground"
: " text-foreground")
)}
target="_blank"
rel="noopener noreferrer"
href={data.node.documentation}
href={data.node?.documentation}
// deactivate link if no documentation is provided
onClick={(event) => {
if (data.node.documentation === "") {
if (data.node?.documentation === "") {
event.preventDefault();
}
}}

View file

@ -4,13 +4,13 @@ import { TabsContext } from "../../contexts/tabsContext";
import { getVersion } from "../../controllers/API";
import Page from "./components/PageComponent";
export default function FlowPage() {
export default function FlowPage(): JSX.Element {
const { flows, tabId, setTabId } = useContext(TabsContext);
const { id } = useParams();
// Set flow tab id
useEffect(() => {
setTabId(id);
setTabId(id!);
}, [id]);
// Initialize state variable for the version
@ -26,7 +26,7 @@ export default function FlowPage() {
{flows.length > 0 &&
tabId !== "" &&
flows.findIndex((flow) => flow.id === tabId) !== -1 && (
<Page flow={flows.find((flow) => flow.id === tabId)} />
<Page flow={flows.find((flow) => flow.id === tabId)!} />
)}
<a
target={"_blank"}

View file

@ -5,7 +5,7 @@ import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { USER_PROJECTS_HEADER } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext";
export default function HomePage() {
export default function HomePage(): JSX.Element {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } =
useContext(TabsContext);
@ -45,7 +45,7 @@ export default function HomePage() {
<Button
variant="primary"
onClick={() => {
addFlow(null, true).then((id) => {
addFlow(null!, true).then((id) => {
navigate("/flow/" + id);
});
}}

View file

@ -0,0 +1,59 @@
import { useState } from "react";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import BaseModal from "../../modals/baseModal";
export default function DeleteAccountPage() {
const [showConfirmation, setShowConfirmation] = useState(false);
const handleDeleteAccount = () => {
// Implement your account deletion logic here
// For example, make an API call to delete the account
// Upon successful deletion, you can redirect the user to another page
console.log("Account deleted!");
// Implement the logic to redirect the user after account deletion.
// For example, use react-router-dom's useHistory hook.
};
return (
<div className="flex h-full w-full flex-col items-center justify-center bg-muted">
<div className="flex w-72 flex-col items-center justify-center gap-2">
<span className="mb-4 text-5xl"></span>
<span className="mb-4 text-center text-2xl font-semibold text-primary">
Delete your account
</span>
<Input className="bg-background" placeholder="Confirm password" />
<BaseModal
open={showConfirmation}
setOpen={setShowConfirmation}
size="x-small"
>
<BaseModal.Header description="This action is irreversible and will permanently erase all your data and information associated with the account. ">
<h3>Are you sure ?</h3>
</BaseModal.Header>
<BaseModal.Trigger>
<Button
variant="default"
className="w-full hover:bg-status-red"
onClick={() => setShowConfirmation(true)}
>
Delete account
</Button>
</BaseModal.Trigger>
<BaseModal.Content>
<div className="flex h-full w-full flex-col justify-end">
<Button
variant="default"
className="w-full hover:bg-status-red"
onClick={() => handleDeleteAccount()}
>
Delete account
</Button>
</div>
</BaseModal.Content>
</BaseModal>
</div>
</div>
);
}

View file

@ -0,0 +1,105 @@
import * as Form from "@radix-ui/react-form";
import { useState } from "react";
import { Link } from "react-router-dom";
import InputComponent from "../../components/inputComponent";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { CONTROL_LOGIN_STATE } from "../../constants/constants";
import {
inputHandlerEventType,
loginInputStateType,
} from "../../types/components";
export default function LoginPage(): JSX.Element {
const [inputState, setInputState] =
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
const { password, username } = inputState;
function handleInput({
target: { name, value },
}: inputHandlerEventType): void {
setInputState((prev) => ({ ...prev, [name]: value }));
}
return (
<Form.Root
onSubmit={(event) => {
if (password === "") {
event.preventDefault();
return;
}
const data = Object.fromEntries(new FormData(event.currentTarget));
event.preventDefault();
}}
className="h-full w-full"
>
<div className="flex h-full w-full flex-col items-center justify-center bg-muted">
<div className="flex w-72 flex-col items-center justify-center gap-2">
<span className="mb-4 text-5xl"></span>
<span className="mb-6 text-2xl font-semibold text-primary">
Sign in to Langflow
</span>
<div className="mb-3 w-full">
<Form.Field name="username">
<Form.Label className="data-[invalid]:label-invalid">
Username <span className="font-medium text-destructive">*</span>
</Form.Label>
<Form.Control asChild>
<Input
onChange={({ target: { value } }) => {
handleInput({ target: { name: "username", value } });
}}
value={username}
className="w-full"
required
placeholder="Username"
/>
</Form.Control>
<Form.Message match="valueMissing" className="field-invalid">
Please enter your username
</Form.Message>
</Form.Field>
</div>
<div className="mb-3 w-full">
<Form.Field name="password">
<Form.Label className="data-[invalid]:label-invalid">
Password <span className="font-medium text-destructive">*</span>
</Form.Label>
<InputComponent
onChange={(value) => {
handleInput({ target: { name: "password", value } });
}}
value={password}
isForm
password={true}
required
placeholder="Password"
className="w-full"
/>
<Form.Message className="field-invalid" match="valueMissing">
Please enter your password
</Form.Message>
</Form.Field>
</div>
<div className="w-full">
<Form.Submit asChild>
<Button className="mr-3 mt-6 w-full">Sign in</Button>
</Form.Submit>
</div>
<div className="w-full">
<Link to="">
<Button className="w-full" variant="outline">
Don't have an account?&nbsp;<b>Sign Up</b>
</Button>
</Link>
</div>
</div>
</div>
</Form.Root>
);
}

Some files were not shown because too many files have changed in this diff Show more