Merge branch 'dev' into db
This commit is contained in:
commit
af0d9456b5
43 changed files with 684 additions and 365 deletions
|
|
@ -1,2 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
make format
|
||||
added_files=$(git diff --name-only --cached --diff-filter=d)
|
||||
|
||||
make format
|
||||
git add ${added_files}
|
||||
2
Makefile
2
Makefile
|
|
@ -59,7 +59,7 @@ lcserve_push:
|
|||
make build_frontend
|
||||
@version=$$(poetry version --short); \
|
||||
lc-serve push --app langflow.lcserve:app --app-dir . \
|
||||
--image-name langflow --image-tag $${version} --verbose
|
||||
--image-name langflow --image-tag $${version} --verbose --public
|
||||
|
||||
lcserve_deploy:
|
||||
@:$(if $(uses),,$(error `uses` is not set. Please run `make uses=... lcserve_deploy`))
|
||||
|
|
|
|||
160
poetry.lock
generated
160
poetry.lock
generated
|
|
@ -148,6 +148,27 @@ files = [
|
|||
{file = "aiostream-0.4.5.tar.gz", hash = "sha256:3ecbf87085230fbcd9605c32ca20c4fb41af02c71d076eab246ea22e35947d88"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anthropic"
|
||||
version = "0.2.10"
|
||||
description = "Library for accessing the anthropic API"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "anthropic-0.2.10-py3-none-any.whl", hash = "sha256:a007496207fd186b0bcb9592b00ca130069d2a427f3d6f602a61dbbd1ac6316e"},
|
||||
{file = "anthropic-0.2.10.tar.gz", hash = "sha256:e4da061a86d8ffb86072c0b0feaf219a3a4f7dfddd4224df9ba769e469498c19"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = "*"
|
||||
httpx = "*"
|
||||
requests = "*"
|
||||
tokenizers = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["black (>=22.3.0)", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
|
|
@ -719,14 +740,14 @@ superset = ["apache-superset (>=1.4.1)"]
|
|||
|
||||
[[package]]
|
||||
name = "cohere"
|
||||
version = "4.6.0"
|
||||
version = "4.7.0"
|
||||
description = ""
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
files = [
|
||||
{file = "cohere-4.6.0-py3-none-any.whl", hash = "sha256:fc60fa73a2d96bdb9f70da4a290d3ede320b74ac01a24c229011049d7cb3511f"},
|
||||
{file = "cohere-4.6.0.tar.gz", hash = "sha256:43218a0a40f6fc023e068732994fb631ce5d160a0bc9f9a3a22524b5932f34ea"},
|
||||
{file = "cohere-4.7.0-py3-none-any.whl", hash = "sha256:ed15621bd271b941110a572cbf6187a4db78cc8d7d9d35a881bffcaeeea21d7c"},
|
||||
{file = "cohere-4.7.0.tar.gz", hash = "sha256:46c674545ad36133a555c6db7f25bdc299e05ec4f77dee707b661a57eb06aea6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -862,31 +883,31 @@ toml = ["tomli"]
|
|||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "40.0.2"
|
||||
version = "41.0.1"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-win32.whl", hash = "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9"},
|
||||
{file = "cryptography-40.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b"},
|
||||
{file = "cryptography-40.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b"},
|
||||
{file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e"},
|
||||
{file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a"},
|
||||
{file = "cryptography-40.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958"},
|
||||
{file = "cryptography-40.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b"},
|
||||
{file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636"},
|
||||
{file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e"},
|
||||
{file = "cryptography-40.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404"},
|
||||
{file = "cryptography-40.0.2.tar.gz", hash = "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-win32.whl", hash = "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5"},
|
||||
{file = "cryptography-41.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c"},
|
||||
{file = "cryptography-41.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb"},
|
||||
{file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3"},
|
||||
{file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039"},
|
||||
{file = "cryptography-41.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc"},
|
||||
{file = "cryptography-41.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485"},
|
||||
{file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c"},
|
||||
{file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a"},
|
||||
{file = "cryptography-41.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5"},
|
||||
{file = "cryptography-41.0.1.tar.gz", hash = "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -895,23 +916,23 @@ cffi = ">=1.12"
|
|||
[package.extras]
|
||||
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
|
||||
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
|
||||
pep8test = ["black", "check-manifest", "mypy", "ruff"]
|
||||
sdist = ["setuptools-rust (>=0.11.4)"]
|
||||
nox = ["nox"]
|
||||
pep8test = ["black", "check-sdist", "mypy", "ruff"]
|
||||
sdist = ["build"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"]
|
||||
test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
|
||||
test-randomorder = ["pytest-randomly"]
|
||||
tox = ["tox"]
|
||||
|
||||
[[package]]
|
||||
name = "ctransformers"
|
||||
version = "0.2.2"
|
||||
version = "0.2.5"
|
||||
description = "Python bindings for the Transformer models implemented in C/C++ using GGML library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "ctransformers-0.2.2-py3-none-any.whl", hash = "sha256:bf682dd0293dd87911c9a9a1169a4873ff55baebc16d465c6029c77f11b18cf6"},
|
||||
{file = "ctransformers-0.2.2.tar.gz", hash = "sha256:1fc36b3fde36d9fd3cb69e48993315bb1f5f54ae552720eb909dc4b3a131c743"},
|
||||
{file = "ctransformers-0.2.5-py3-none-any.whl", hash = "sha256:5e0ee7d2be2cb1d627a702acdbf1f4f3c9c97d706e9d7f59a13079c1836a1432"},
|
||||
{file = "ctransformers-0.2.5.tar.gz", hash = "sha256:b813f19d5c2249b75422ae3188b1c834aeb8a095800df32328559a740acdb404"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1044,14 +1065,14 @@ weaviate = ["weaviate-client (>=3.9.0,<3.10.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "docker"
|
||||
version = "6.1.2"
|
||||
version = "6.1.3"
|
||||
description = "A Python library for the Docker Engine API."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "docker-6.1.2-py3-none-any.whl", hash = "sha256:134cd828f84543cbf8e594ff81ca90c38288df3c0a559794c12f2e4b634ea19e"},
|
||||
{file = "docker-6.1.2.tar.gz", hash = "sha256:dcc088adc2ec4e7cfc594e275d8bd2c9738c56c808de97476939ef67db5af8c2"},
|
||||
{file = "docker-6.1.3-py3-none-any.whl", hash = "sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9"},
|
||||
{file = "docker-6.1.3.tar.gz", hash = "sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1192,6 +1213,41 @@ files = [
|
|||
[package.extras]
|
||||
tests = ["asttokens", "littleutils", "pytest", "rich"]
|
||||
|
||||
[[package]]
|
||||
name = "faiss-cpu"
|
||||
version = "1.7.4"
|
||||
description = "A library for efficient similarity search and clustering of dense vectors."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "faiss-cpu-1.7.4.tar.gz", hash = "sha256:265dc31b0c079bf4433303bf6010f73922490adff9188b915e2d3f5e9c82dd0a"},
|
||||
{file = "faiss_cpu-1.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50d4ebe7f1869483751c558558504f818980292a9b55be36f9a1ee1009d9a686"},
|
||||
{file = "faiss_cpu-1.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b1db7fae7bd8312aeedd0c41536bcd19a6e297229e1dce526bde3a73ab8c0b5"},
|
||||
{file = "faiss_cpu-1.7.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17b7fa7194a228a84929d9e6619d0e7dbf00cc0f717e3462253766f5e3d07de8"},
|
||||
{file = "faiss_cpu-1.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dca531952a2e3eac56f479ff22951af4715ee44788a3fe991d208d766d3f95f3"},
|
||||
{file = "faiss_cpu-1.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:7173081d605e74766f950f2e3d6568a6f00c53f32fd9318063e96728c6c62821"},
|
||||
{file = "faiss_cpu-1.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0bbd6f55d7940cc0692f79e32a58c66106c3c950cee2341b05722de9da23ea3"},
|
||||
{file = "faiss_cpu-1.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e13c14280376100f143767d0efe47dcb32618f69e62bbd3ea5cd38c2e1755926"},
|
||||
{file = "faiss_cpu-1.7.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c521cb8462f3b00c0c7dfb11caff492bb67816528b947be28a3b76373952c41d"},
|
||||
{file = "faiss_cpu-1.7.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afdd9fe1141117fed85961fd36ee627c83fc3b9fd47bafb52d3c849cc2f088b7"},
|
||||
{file = "faiss_cpu-1.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:2ff7f57889ea31d945e3b87275be3cad5d55b6261a4e3f51c7aba304d76b81fb"},
|
||||
{file = "faiss_cpu-1.7.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eeaf92f27d76249fb53c1adafe617b0f217ab65837acf7b4ec818511caf6e3d8"},
|
||||
{file = "faiss_cpu-1.7.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:102b1bd763e9b0c281ac312590af3eaf1c8b663ccbc1145821fe6a9f92b8eaaf"},
|
||||
{file = "faiss_cpu-1.7.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5512da6707c967310c46ff712b00418b7ae28e93cb609726136e826e9f2f14fa"},
|
||||
{file = "faiss_cpu-1.7.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0c2e5b9d8c28c99f990e87379d5bbcc6c914da91ebb4250166864fd12db5755b"},
|
||||
{file = "faiss_cpu-1.7.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:43f67f325393145d360171cd98786fcea6120ce50397319afd3bb78be409fb8a"},
|
||||
{file = "faiss_cpu-1.7.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6a4e4af194b8fce74c4b770cad67ad1dd1b4673677fc169723e4c50ba5bd97a8"},
|
||||
{file = "faiss_cpu-1.7.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31bfb7b9cffc36897ae02a983e04c09fe3b8c053110a287134751a115334a1df"},
|
||||
{file = "faiss_cpu-1.7.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52d7de96abef2340c0d373c1f5cbc78026a3cebb0f8f3a5920920a00210ead1f"},
|
||||
{file = "faiss_cpu-1.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:699feef85b23c2c729d794e26ca69bebc0bee920d676028c06fd0e0becc15c7e"},
|
||||
{file = "faiss_cpu-1.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:559a0133f5ed44422acb09ee1ac0acffd90c6666d1bc0d671c18f6e93ad603e2"},
|
||||
{file = "faiss_cpu-1.7.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1d71539fe3dc0f1bed41ef954ca701678776f231046bf0ca22ccea5cf5bef6"},
|
||||
{file = "faiss_cpu-1.7.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12d45e0157024eb3249842163162983a1ac8b458f1a8b17bbf86f01be4585a99"},
|
||||
{file = "faiss_cpu-1.7.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f0eab359e066d32c874f51a7d4bf6440edeec068b7fe47e6d803c73605a8b4c"},
|
||||
{file = "faiss_cpu-1.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:98459ceeeb735b9df1a5b94572106ffe0a6ce740eb7e4626715dd218657bb4dc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-useragent"
|
||||
version = "1.1.3"
|
||||
|
|
@ -1209,14 +1265,14 @@ importlib-resources = {version = ">=5.0", markers = "python_version < \"3.10\""}
|
|||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.95.2"
|
||||
version = "0.96.0"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "fastapi-0.95.2-py3-none-any.whl", hash = "sha256:d374dbc4ef2ad9b803899bd3360d34c534adc574546e25314ab72c0c4411749f"},
|
||||
{file = "fastapi-0.95.2.tar.gz", hash = "sha256:4d9d3e8c71c73f11874bcf5e33626258d143252e329a01002f767306c64fb982"},
|
||||
{file = "fastapi-0.96.0-py3-none-any.whl", hash = "sha256:b8e11fe81e81eab4e1504209917338e0b80f783878a42c2b99467e5e1019a1e9"},
|
||||
{file = "fastapi-0.96.0.tar.gz", hash = "sha256:71232d47c2787446991c81c41c249f8a16238d52d779c0e6b43927d3773dbe3c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -1385,14 +1441,14 @@ uritemplate = ">=3.0.1,<5"
|
|||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "2.19.0"
|
||||
version = "2.19.1"
|
||||
description = "Google Authentication Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "google-auth-2.19.0.tar.gz", hash = "sha256:f39d528077ac540793dd3c22a8706178f157642a67d874db25c640b7fead277e"},
|
||||
{file = "google_auth-2.19.0-py2.py3-none-any.whl", hash = "sha256:be617bfaf77774008e9d177573f782e109188c8a64ae6e744285df5cea3e7df6"},
|
||||
{file = "google-auth-2.19.1.tar.gz", hash = "sha256:a9cfa88b3e16196845e64a3658eb953992129d13ac7337b064c6546f77c17183"},
|
||||
{file = "google_auth-2.19.1-py2.py3-none-any.whl", hash = "sha256:ea165e014c7cbd496558796b627c271aa8c18b4cba79dc1cc962b24c5efdfb85"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2035,14 +2091,14 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio"
|
|||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "8.13.2"
|
||||
version = "8.14.0"
|
||||
description = "IPython: Productive Interactive Computing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "ipython-8.13.2-py3-none-any.whl", hash = "sha256:ffca270240fbd21b06b2974e14a86494d6d29290184e788275f55e0b55914926"},
|
||||
{file = "ipython-8.13.2.tar.gz", hash = "sha256:7dff3fad32b97f6488e02f87b970f309d082f758d7b7fc252e3b19ee0e432dbb"},
|
||||
{file = "ipython-8.14.0-py3-none-any.whl", hash = "sha256:248aca623f5c99a6635bc3857677b7320b9b8039f99f070ee0d20a5ca5a8e6bf"},
|
||||
{file = "ipython-8.14.0.tar.gz", hash = "sha256:1d197b907b6ba441b692c48cf2a3a2de280dc0ac91a3405b39349a50272ca0a1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -2374,13 +2430,13 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "langchain-serve"
|
||||
version = "0.0.38"
|
||||
version = "0.0.41"
|
||||
description = "Langchain Serve - serve your langchain apps on Jina AI Cloud."
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "langchain-serve-0.0.38.tar.gz", hash = "sha256:649b8e26eebe6b33960c081b388fb7118acbfdc00f97dd935a580ab88aca53d6"},
|
||||
{file = "langchain-serve-0.0.41.tar.gz", hash = "sha256:fcf0d3ac9e48b5b24825ae2e1d8a383795c5744b18ef61cb74a99020a9e5d46a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -4677,14 +4733,14 @@ idna2008 = ["idna"]
|
|||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.3.5"
|
||||
version = "13.4.1"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "rich-13.3.5-py3-none-any.whl", hash = "sha256:69cdf53799e63f38b95b9bf9c875f8c90e78dd62b2f00c13a911c7a3b9fa4704"},
|
||||
{file = "rich-13.3.5.tar.gz", hash = "sha256:2d11b9b8dd03868f09b4fffadc84a6a8cda574e40dc90821bd845720ebb8e89c"},
|
||||
{file = "rich-13.4.1-py3-none-any.whl", hash = "sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75"},
|
||||
{file = "rich-13.4.1.tar.gz", hash = "sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -5127,14 +5183,14 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"]
|
|||
|
||||
[[package]]
|
||||
name = "textual"
|
||||
version = "0.26.0"
|
||||
version = "0.27.0"
|
||||
description = "Modern Text User Interface framework"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = ">=3.7,<4.0"
|
||||
files = [
|
||||
{file = "textual-0.26.0-py3-none-any.whl", hash = "sha256:1efd04e9f61b3e95fd1c65436d3262f99e3f86cdeb524d13045bb551eb615c02"},
|
||||
{file = "textual-0.26.0.tar.gz", hash = "sha256:78094c83017d2836b726513abdf434cc034a0e68cc45e63b3b056c9b8b7fa673"},
|
||||
{file = "textual-0.27.0-py3-none-any.whl", hash = "sha256:dc45eaf7da330686c56d6f76f59d05fd216ce6aad90fa44ee269881efc622151"},
|
||||
{file = "textual-0.27.0.tar.gz", hash = "sha256:8bdcb09dc35a706ef939b1276ccfdec10eaaee6147b41cb7587cf33298a8dd33"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -6207,4 +6263,4 @@ deploy = ["langchain-serve"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.12"
|
||||
content-hash = "a293ac634ddc277680a1293b396f84edd897946d92b04bdff9f35b14c935a8f8"
|
||||
content-hash = "4cac7dea0c1222711ba7eed82d5716d5e361d454edb6e0299b387fbb115d2c3d"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow"
|
||||
version = "0.0.79"
|
||||
version = "0.0.80"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Logspace <contact@logspace.ai>"]
|
||||
maintainers = [
|
||||
|
|
@ -22,7 +22,7 @@ langflow = "langflow.__main__:main"
|
|||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.12"
|
||||
fastapi = "^0.95.0"
|
||||
fastapi = "^0.96.0"
|
||||
uvicorn = "^0.20.0"
|
||||
beautifulsoup4 = "^4.11.2"
|
||||
google-search-results = "^2.4.1"
|
||||
|
|
@ -49,7 +49,7 @@ psycopg2-binary = "^2.9.6"
|
|||
pyarrow = "^11.0.0"
|
||||
tiktoken = "^0.3.3"
|
||||
wikipedia = "^1.4.0"
|
||||
langchain-serve = { version = "^0.0.38", optional = true }
|
||||
langchain-serve = { version = ">0.0.39", optional = true }
|
||||
qdrant-client = "^1.2.0"
|
||||
websockets = "^11.0.3"
|
||||
weaviate-client = "^3.19.2"
|
||||
|
|
@ -58,6 +58,8 @@ sentence-transformers = "^2.2.2"
|
|||
ctransformers = "^0.2.2"
|
||||
cohere = "^4.6.0"
|
||||
sqlmodel = "^0.0.8"
|
||||
faiss-cpu = "^1.7.4"
|
||||
anthropic = "^0.2.9"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from langflow.api.base import (
|
|||
PromptValidationResponse,
|
||||
validate_prompt,
|
||||
)
|
||||
from langflow.graph.nodes import VectorStoreNode
|
||||
from langflow.graph.vertex.types import VectorStoreVertex
|
||||
from langflow.interface.run import build_graph
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.validate import validate_code
|
||||
|
|
@ -49,7 +49,7 @@ def post_validate_node(node_id: str, data: dict):
|
|||
node = graph.get_node(node_id)
|
||||
if node is None:
|
||||
raise ValueError(f"Node {node_id} not found")
|
||||
if not isinstance(node, VectorStoreNode):
|
||||
if not isinstance(node, VectorStoreVertex):
|
||||
node.build()
|
||||
return json.dumps({"valid": True, "params": str(node._built_object_repr())})
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ llms:
|
|||
- LlamaCpp
|
||||
- CTransformers
|
||||
- Cohere
|
||||
- Anthropic
|
||||
- ChatAnthropic
|
||||
memories:
|
||||
- ConversationBufferMemory
|
||||
- ConversationSummaryMemory
|
||||
|
|
@ -79,7 +81,7 @@ tools:
|
|||
- Calculator
|
||||
- Serper Search
|
||||
- Tool
|
||||
- PythonFunction
|
||||
- PythonFunctionTool
|
||||
- JsonSpec
|
||||
- News API
|
||||
- TMDB API
|
||||
|
|
@ -118,6 +120,7 @@ vectorstores:
|
|||
- Chroma
|
||||
- Qdrant
|
||||
- Weaviate
|
||||
- FAISS
|
||||
wrappers:
|
||||
- RequestsWrapper
|
||||
# - ChatPromptTemplate
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langflow.template import frontend_node
|
|||
CUSTOM_NODES = {
|
||||
"prompts": {"ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode()},
|
||||
"tools": {
|
||||
"PythonFunction": frontend_node.tools.PythonFunctionNode(),
|
||||
"PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(),
|
||||
"Tool": frontend_node.tools.ToolNode(),
|
||||
},
|
||||
"agents": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,35 @@
|
|||
from langflow.graph.base import Edge, Node
|
||||
from langflow.graph.graph import Graph
|
||||
from langflow.graph.edge.base import Edge
|
||||
from langflow.graph.graph.base import Graph
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.graph.vertex.types import (
|
||||
AgentVertex,
|
||||
ChainVertex,
|
||||
DocumentLoaderVertex,
|
||||
EmbeddingVertex,
|
||||
LLMVertex,
|
||||
MemoryVertex,
|
||||
PromptVertex,
|
||||
TextSplitterVertex,
|
||||
ToolVertex,
|
||||
ToolkitVertex,
|
||||
VectorStoreVertex,
|
||||
WrapperVertex,
|
||||
)
|
||||
|
||||
__all__ = ["Graph", "Node", "Edge"]
|
||||
__all__ = [
|
||||
"Graph",
|
||||
"Vertex",
|
||||
"Edge",
|
||||
"AgentVertex",
|
||||
"ChainVertex",
|
||||
"DocumentLoaderVertex",
|
||||
"EmbeddingVertex",
|
||||
"LLMVertex",
|
||||
"MemoryVertex",
|
||||
"PromptVertex",
|
||||
"TextSplitterVertex",
|
||||
"ToolVertex",
|
||||
"ToolkitVertex",
|
||||
"VectorStoreVertex",
|
||||
"WrapperVertex",
|
||||
]
|
||||
|
|
|
|||
0
src/backend/langflow/graph/edge/__init__.py
Normal file
0
src/backend/langflow/graph/edge/__init__.py
Normal file
52
src/backend/langflow/graph/edge/base.py
Normal file
52
src/backend/langflow/graph/edge/base.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
from langflow.utils.logger import logger
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
|
||||
|
||||
class Edge:
|
||||
def __init__(self, source: "Vertex", target: "Vertex"):
|
||||
self.source: "Vertex" = source
|
||||
self.target: "Vertex" = target
|
||||
self.validate_edge()
|
||||
|
||||
def validate_edge(self) -> None:
|
||||
# Validate that the outputs of the source node are valid inputs
|
||||
# for the target node
|
||||
self.source_types = self.source.output
|
||||
self.target_reqs = self.target.required_inputs + self.target.optional_inputs
|
||||
# Both lists contain strings and sometimes a string contains the value we are
|
||||
# looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"]
|
||||
# so we need to check if any of the strings in source_types is in target_reqs
|
||||
self.valid = any(
|
||||
output in target_req
|
||||
for output in self.source_types
|
||||
for target_req in self.target_reqs
|
||||
)
|
||||
# Get what type of input the target node is expecting
|
||||
|
||||
self.matched_type = next(
|
||||
(
|
||||
output
|
||||
for output in self.source_types
|
||||
for target_req in self.target_reqs
|
||||
if output in target_req
|
||||
),
|
||||
None,
|
||||
)
|
||||
no_matched_type = self.matched_type is None
|
||||
if no_matched_type:
|
||||
logger.debug(self.source_types)
|
||||
logger.debug(self.target_reqs)
|
||||
if no_matched_type:
|
||||
raise ValueError(
|
||||
f"Edge between {self.source.vertex_type} and {self.target.vertex_type} "
|
||||
f"has no matched type"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"Edge(source={self.source.id}, target={self.target.id}, valid={self.valid}"
|
||||
f", matched_type={self.matched_type})"
|
||||
)
|
||||
0
src/backend/langflow/graph/graph/__init__.py
Normal file
0
src/backend/langflow/graph/graph/__init__.py
Normal file
|
|
@ -1,38 +1,20 @@
|
|||
from typing import Dict, List, Type, Union
|
||||
|
||||
from langflow.graph.base import Edge, Node
|
||||
from langflow.graph.nodes import (
|
||||
AgentNode,
|
||||
ChainNode,
|
||||
DocumentLoaderNode,
|
||||
EmbeddingNode,
|
||||
FileToolNode,
|
||||
LLMNode,
|
||||
MemoryNode,
|
||||
PromptNode,
|
||||
TextSplitterNode,
|
||||
ToolkitNode,
|
||||
ToolNode,
|
||||
VectorStoreNode,
|
||||
WrapperNode,
|
||||
from langflow.graph.edge.base import Edge
|
||||
from langflow.graph.graph.constants import VERTEX_TYPE_MAP
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.graph.vertex.types import (
|
||||
FileToolVertex,
|
||||
LLMVertex,
|
||||
ToolkitVertex,
|
||||
)
|
||||
from langflow.interface.agents.base import agent_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.document_loaders.base import documentloader_creator
|
||||
from langflow.interface.embeddings.base import embedding_creator
|
||||
from langflow.interface.llms.base import llm_creator
|
||||
from langflow.interface.memories.base import memory_creator
|
||||
from langflow.interface.prompts.base import prompt_creator
|
||||
from langflow.interface.text_splitters.base import textsplitter_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.tools.base import tool_creator
|
||||
from langflow.interface.tools.constants import FILE_TOOLS
|
||||
from langflow.interface.vector_store.base import vectorstore_creator
|
||||
from langflow.interface.wrappers.base import wrapper_creator
|
||||
from langflow.utils import payload
|
||||
|
||||
|
||||
class Graph:
|
||||
"""A class representing a graph of nodes and edges."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
nodes: List[Dict[str, Union[str, Dict[str, Union[str, List[str]]]]]],
|
||||
|
|
@ -43,7 +25,8 @@ class Graph:
|
|||
self._build_graph()
|
||||
|
||||
def _build_graph(self) -> None:
|
||||
self.nodes = self._build_nodes()
|
||||
"""Builds the graph from the nodes and edges."""
|
||||
self.nodes = self._build_vertices()
|
||||
self.edges = self._build_edges()
|
||||
for edge in self.edges:
|
||||
edge.source.add_edge(edge)
|
||||
|
|
@ -51,17 +34,25 @@ class Graph:
|
|||
|
||||
# This is a hack to make sure that the LLM node is sent to
|
||||
# the toolkit node
|
||||
self._build_node_params()
|
||||
# remove invalid nodes
|
||||
self._remove_invalid_nodes()
|
||||
|
||||
def _build_node_params(self) -> None:
|
||||
"""Identifies and handles the LLM node within the graph."""
|
||||
llm_node = None
|
||||
for node in self.nodes:
|
||||
node._build_params()
|
||||
|
||||
if isinstance(node, LLMNode):
|
||||
if isinstance(node, LLMVertex):
|
||||
llm_node = node
|
||||
|
||||
for node in self.nodes:
|
||||
if isinstance(node, ToolkitNode):
|
||||
node.params["llm"] = llm_node
|
||||
# remove invalid nodes
|
||||
if llm_node:
|
||||
for node in self.nodes:
|
||||
if isinstance(node, ToolkitVertex):
|
||||
node.params["llm"] = llm_node
|
||||
|
||||
def _remove_invalid_nodes(self) -> None:
|
||||
"""Removes invalid nodes from the graph."""
|
||||
self.nodes = [
|
||||
node
|
||||
for node in self.nodes
|
||||
|
|
@ -69,28 +60,33 @@ class Graph:
|
|||
or (len(self.nodes) == 1 and len(self.edges) == 0)
|
||||
]
|
||||
|
||||
def _validate_node(self, node: Node) -> bool:
|
||||
def _validate_node(self, node: Vertex) -> bool:
|
||||
"""Validates a node."""
|
||||
# All nodes that do not have edges are invalid
|
||||
return len(node.edges) > 0
|
||||
|
||||
def get_node(self, node_id: str) -> Union[None, Node]:
|
||||
def get_node(self, node_id: str) -> Union[None, Vertex]:
|
||||
"""Returns a node by id."""
|
||||
return next((node for node in self.nodes if node.id == node_id), None)
|
||||
|
||||
def get_nodes_with_target(self, node: Node) -> List[Node]:
|
||||
connected_nodes: List[Node] = [
|
||||
def get_nodes_with_target(self, node: Vertex) -> List[Vertex]:
|
||||
"""Returns the nodes connected to a node."""
|
||||
connected_nodes: List[Vertex] = [
|
||||
edge.source for edge in self.edges if edge.target == node
|
||||
]
|
||||
return connected_nodes
|
||||
|
||||
def build(self) -> List[Node]:
|
||||
def build(self) -> List[Vertex]:
|
||||
"""Builds the graph."""
|
||||
# Get root node
|
||||
root_node = payload.get_root_node(self)
|
||||
if root_node is None:
|
||||
raise ValueError("No root node found")
|
||||
return root_node.build()
|
||||
|
||||
def get_node_neighbors(self, node: Node) -> Dict[Node, int]:
|
||||
neighbors: Dict[Node, int] = {}
|
||||
def get_node_neighbors(self, node: Vertex) -> Dict[Vertex, int]:
|
||||
"""Returns the neighbors of a node."""
|
||||
neighbors: Dict[Vertex, int] = {}
|
||||
for edge in self.edges:
|
||||
if edge.source == node:
|
||||
neighbor = edge.target
|
||||
|
|
@ -105,6 +101,7 @@ class Graph:
|
|||
return neighbors
|
||||
|
||||
def _build_edges(self) -> List[Edge]:
|
||||
"""Builds the edges of the graph."""
|
||||
# Edge takes two nodes as arguments, so we need to build the nodes first
|
||||
# and then build the edges
|
||||
# if we can't find a node, we raise an error
|
||||
|
|
@ -120,43 +117,31 @@ class Graph:
|
|||
edges.append(Edge(source, target))
|
||||
return edges
|
||||
|
||||
def _get_node_class(self, node_type: str, node_lc_type: str) -> Type[Node]:
|
||||
node_type_map: Dict[str, Type[Node]] = {
|
||||
**{t: PromptNode for t in prompt_creator.to_list()},
|
||||
**{t: AgentNode for t in agent_creator.to_list()},
|
||||
**{t: ChainNode for t in chain_creator.to_list()},
|
||||
**{t: ToolNode for t in tool_creator.to_list()},
|
||||
**{t: ToolkitNode for t in toolkits_creator.to_list()},
|
||||
**{t: WrapperNode for t in wrapper_creator.to_list()},
|
||||
**{t: LLMNode for t in llm_creator.to_list()},
|
||||
**{t: MemoryNode for t in memory_creator.to_list()},
|
||||
**{t: EmbeddingNode for t in embedding_creator.to_list()},
|
||||
**{t: VectorStoreNode for t in vectorstore_creator.to_list()},
|
||||
**{t: DocumentLoaderNode for t in documentloader_creator.to_list()},
|
||||
**{t: TextSplitterNode for t in textsplitter_creator.to_list()},
|
||||
}
|
||||
|
||||
def _get_vertex_class(self, node_type: str, node_lc_type: str) -> Type[Vertex]:
|
||||
"""Returns the node class based on the node type."""
|
||||
if node_type in FILE_TOOLS:
|
||||
return FileToolNode
|
||||
if node_type in node_type_map:
|
||||
return node_type_map[node_type]
|
||||
if node_lc_type in node_type_map:
|
||||
return node_type_map[node_lc_type]
|
||||
return Node
|
||||
return FileToolVertex
|
||||
if node_type in VERTEX_TYPE_MAP:
|
||||
return VERTEX_TYPE_MAP[node_type]
|
||||
return (
|
||||
VERTEX_TYPE_MAP[node_lc_type] if node_lc_type in VERTEX_TYPE_MAP else Vertex
|
||||
)
|
||||
|
||||
def _build_nodes(self) -> List[Node]:
|
||||
nodes: List[Node] = []
|
||||
def _build_vertices(self) -> List[Vertex]:
|
||||
"""Builds the vertices of the graph."""
|
||||
nodes: List[Vertex] = []
|
||||
for node in self._nodes:
|
||||
node_data = node["data"]
|
||||
node_type: str = node_data["type"] # type: ignore
|
||||
node_lc_type: str = node_data["node"]["template"]["_type"] # type: ignore
|
||||
|
||||
NodeClass = self._get_node_class(node_type, node_lc_type)
|
||||
nodes.append(NodeClass(node))
|
||||
VertexClass = self._get_vertex_class(node_type, node_lc_type)
|
||||
nodes.append(VertexClass(node))
|
||||
|
||||
return nodes
|
||||
|
||||
def get_children_by_node_type(self, node: Node, node_type: str) -> List[Node]:
|
||||
def get_children_by_node_type(self, node: Vertex, node_type: str) -> List[Vertex]:
|
||||
"""Returns the children of a node based on the node type."""
|
||||
children = []
|
||||
node_types = [node.data["type"]]
|
||||
if "node" in node.data:
|
||||
49
src/backend/langflow/graph/graph/constants.py
Normal file
49
src/backend/langflow/graph/graph/constants.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.graph.vertex.types import (
|
||||
AgentVertex,
|
||||
ChainVertex,
|
||||
DocumentLoaderVertex,
|
||||
EmbeddingVertex,
|
||||
LLMVertex,
|
||||
MemoryVertex,
|
||||
PromptVertex,
|
||||
TextSplitterVertex,
|
||||
ToolVertex,
|
||||
ToolkitVertex,
|
||||
VectorStoreVertex,
|
||||
WrapperVertex,
|
||||
)
|
||||
from langflow.interface.agents.base import agent_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.document_loaders.base import documentloader_creator
|
||||
from langflow.interface.embeddings.base import embedding_creator
|
||||
from langflow.interface.llms.base import llm_creator
|
||||
from langflow.interface.memories.base import memory_creator
|
||||
from langflow.interface.prompts.base import prompt_creator
|
||||
from langflow.interface.text_splitters.base import textsplitter_creator
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.tools.base import tool_creator
|
||||
from langflow.interface.vector_store.base import vectorstore_creator
|
||||
from langflow.interface.wrappers.base import wrapper_creator
|
||||
|
||||
|
||||
from typing import Dict, Type
|
||||
|
||||
|
||||
DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"]
|
||||
|
||||
|
||||
VERTEX_TYPE_MAP: Dict[str, Type[Vertex]] = {
|
||||
**{t: PromptVertex for t in prompt_creator.to_list()},
|
||||
**{t: AgentVertex for t in agent_creator.to_list()},
|
||||
**{t: ChainVertex for t in chain_creator.to_list()},
|
||||
**{t: ToolVertex for t in tool_creator.to_list()},
|
||||
**{t: ToolkitVertex for t in toolkits_creator.to_list()},
|
||||
**{t: WrapperVertex for t in wrapper_creator.to_list()},
|
||||
**{t: LLMVertex for t in llm_creator.to_list()},
|
||||
**{t: MemoryVertex for t in memory_creator.to_list()},
|
||||
**{t: EmbeddingVertex for t in embedding_creator.to_list()},
|
||||
**{t: VectorStoreVertex for t in vectorstore_creator.to_list()},
|
||||
**{t: DocumentLoaderVertex for t in documentloader_creator.to_list()},
|
||||
**{t: TextSplitterVertex for t in textsplitter_creator.to_list()},
|
||||
}
|
||||
0
src/backend/langflow/graph/vertex/__init__.py
Normal file
0
src/backend/langflow/graph/vertex/__init__.py
Normal file
|
|
@ -1,27 +1,27 @@
|
|||
# Description: Graph class for building a graph of nodes and edges
|
||||
# Insights:
|
||||
# - Defer prompts building to the last moment or when they have all the tools
|
||||
# - Build each inner agent first, then build the outer agent
|
||||
|
||||
import contextlib
|
||||
import inspect
|
||||
import types
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from langflow.cache import base as cache_utils
|
||||
from langflow.graph.constants import DIRECT_TYPES
|
||||
from langflow.graph.vertex.constants import DIRECT_TYPES
|
||||
from langflow.interface import loading
|
||||
from langflow.interface.listing import ALL_TYPES_DICT
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.util import sync_to_async
|
||||
|
||||
|
||||
class Node:
|
||||
import contextlib
|
||||
import inspect
|
||||
import types
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.graph.edge.base import Edge
|
||||
|
||||
|
||||
class Vertex:
|
||||
def __init__(self, data: Dict, base_type: Optional[str] = None) -> None:
|
||||
self.id: str = data["id"]
|
||||
self._data = data
|
||||
self.edges: List[Edge] = []
|
||||
self.edges: List["Edge"] = []
|
||||
self.base_type: Optional[str] = base_type
|
||||
self._parse_data()
|
||||
self._built_object = None
|
||||
|
|
@ -48,12 +48,12 @@ class Node:
|
|||
]
|
||||
|
||||
template_dict = self.data["node"]["template"]
|
||||
self.node_type = (
|
||||
self.vertex_type = (
|
||||
self.data["type"] if "Tool" not in self.output else template_dict["_type"]
|
||||
)
|
||||
if self.base_type is None:
|
||||
for base_type, value in ALL_TYPES_DICT.items():
|
||||
if self.node_type in value:
|
||||
if self.vertex_type in value:
|
||||
self.base_type = base_type
|
||||
break
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ class Node:
|
|||
if value["required"] and not edges:
|
||||
# If a required parameter is not found, raise an error
|
||||
raise ValueError(
|
||||
f"Required input {key} for module {self.node_type} not found"
|
||||
f"Required input {key} for module {self.vertex_type} not found"
|
||||
)
|
||||
elif value["list"]:
|
||||
# If this is a list parameter, append all sources to a list
|
||||
|
|
@ -128,7 +128,7 @@ class Node:
|
|||
# so we need to check if value has value
|
||||
new_value = value.get("value")
|
||||
if new_value is None:
|
||||
warnings.warn(f"Value for {key} in {self.node_type} is None. ")
|
||||
warnings.warn(f"Value for {key} in {self.vertex_type} is None. ")
|
||||
if value.get("type") == "int":
|
||||
with contextlib.suppress(TypeError, ValueError):
|
||||
new_value = int(new_value) # type: ignore
|
||||
|
|
@ -148,12 +148,12 @@ class Node:
|
|||
# and continue
|
||||
# Another aspect is that the node_type is the class that we need to import
|
||||
# and instantiate with these built params
|
||||
logger.debug(f"Building {self.node_type}")
|
||||
logger.debug(f"Building {self.vertex_type}")
|
||||
# Build each node in the params dict
|
||||
for key, value in self.params.copy().items():
|
||||
# Check if Node or list of Nodes and not self
|
||||
# to avoid recursion
|
||||
if isinstance(value, Node):
|
||||
if isinstance(value, Vertex):
|
||||
if value == self:
|
||||
del self.params[key]
|
||||
continue
|
||||
|
|
@ -177,7 +177,7 @@ class Node:
|
|||
|
||||
self.params[key] = result
|
||||
elif isinstance(value, list) and all(
|
||||
isinstance(node, Node) for node in value
|
||||
isinstance(node, Vertex) for node in value
|
||||
):
|
||||
self.params[key] = []
|
||||
for node in value:
|
||||
|
|
@ -193,17 +193,17 @@ class Node:
|
|||
|
||||
try:
|
||||
self._built_object = loading.instantiate_class(
|
||||
node_type=self.node_type,
|
||||
node_type=self.vertex_type,
|
||||
base_type=self.base_type,
|
||||
params=self.params,
|
||||
)
|
||||
except Exception as exc:
|
||||
raise ValueError(
|
||||
f"Error building node {self.node_type}: {str(exc)}"
|
||||
f"Error building node {self.vertex_type}: {str(exc)}"
|
||||
) from exc
|
||||
|
||||
if self._built_object is None:
|
||||
raise ValueError(f"Node type {self.node_type} not found")
|
||||
raise ValueError(f"Node type {self.vertex_type} not found")
|
||||
|
||||
self._built = True
|
||||
|
||||
|
|
@ -220,57 +220,10 @@ class Node:
|
|||
return f"Node(id={self.id}, data={self.data})"
|
||||
|
||||
def __eq__(self, __o: object) -> bool:
|
||||
return self.id == __o.id if isinstance(__o, Node) else False
|
||||
return self.id == __o.id if isinstance(__o, Vertex) else False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return id(self)
|
||||
|
||||
def _built_object_repr(self):
|
||||
return repr(self._built_object)
|
||||
|
||||
|
||||
class Edge:
|
||||
def __init__(self, source: "Node", target: "Node"):
|
||||
self.source: "Node" = source
|
||||
self.target: "Node" = target
|
||||
self.validate_edge()
|
||||
|
||||
def validate_edge(self) -> None:
|
||||
# Validate that the outputs of the source node are valid inputs
|
||||
# for the target node
|
||||
self.source_types = self.source.output
|
||||
self.target_reqs = self.target.required_inputs + self.target.optional_inputs
|
||||
# Both lists contain strings and sometimes a string contains the value we are
|
||||
# looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"]
|
||||
# so we need to check if any of the strings in source_types is in target_reqs
|
||||
self.valid = any(
|
||||
output in target_req
|
||||
for output in self.source_types
|
||||
for target_req in self.target_reqs
|
||||
)
|
||||
# Get what type of input the target node is expecting
|
||||
|
||||
self.matched_type = next(
|
||||
(
|
||||
output
|
||||
for output in self.source_types
|
||||
for target_req in self.target_reqs
|
||||
if output in target_req
|
||||
),
|
||||
None,
|
||||
)
|
||||
no_matched_type = self.matched_type is None
|
||||
if no_matched_type:
|
||||
logger.debug(self.source_types)
|
||||
logger.debug(self.target_reqs)
|
||||
if no_matched_type:
|
||||
raise ValueError(
|
||||
f"Edge between {self.source.node_type} and {self.target.node_type} "
|
||||
f"has no matched type"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"Edge(source={self.source.id}, target={self.target.id}, valid={self.valid}"
|
||||
f", matched_type={self.matched_type})"
|
||||
)
|
||||
|
|
@ -1,22 +1,22 @@
|
|||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from langflow.graph.base import Node
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.graph.utils import extract_input_variables_from_prompt
|
||||
|
||||
|
||||
class AgentNode(Node):
|
||||
class AgentVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="agents")
|
||||
|
||||
self.tools: List[ToolNode] = []
|
||||
self.chains: List[ChainNode] = []
|
||||
self.tools: List[ToolVertex] = []
|
||||
self.chains: List[ChainVertex] = []
|
||||
|
||||
def _set_tools_and_chains(self) -> None:
|
||||
for edge in self.edges:
|
||||
source_node = edge.source
|
||||
if isinstance(source_node, ToolNode):
|
||||
if isinstance(source_node, ToolVertex):
|
||||
self.tools.append(source_node)
|
||||
elif isinstance(source_node, ChainNode):
|
||||
elif isinstance(source_node, ChainVertex):
|
||||
self.chains.append(source_node)
|
||||
|
||||
def build(self, force: bool = False) -> Any:
|
||||
|
|
@ -33,24 +33,28 @@ class AgentNode(Node):
|
|||
self._build()
|
||||
|
||||
#! Cannot deepcopy VectorStore, VectorStoreRouter, or SQL agents
|
||||
if self.node_type in ["VectorStoreAgent", "VectorStoreRouterAgent", "SQLAgent"]:
|
||||
if self.vertex_type in [
|
||||
"VectorStoreAgent",
|
||||
"VectorStoreRouterAgent",
|
||||
"SQLAgent",
|
||||
]:
|
||||
return self._built_object
|
||||
return self._built_object
|
||||
|
||||
|
||||
class ToolNode(Node):
|
||||
class ToolVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="tools")
|
||||
|
||||
|
||||
class PromptNode(Node):
|
||||
class PromptVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="prompts")
|
||||
|
||||
def build(
|
||||
self,
|
||||
force: bool = False,
|
||||
tools: Optional[Union[List[Node], List[ToolNode]]] = None,
|
||||
tools: Optional[Union[List[Vertex], List[ToolVertex]]] = None,
|
||||
) -> Any:
|
||||
if not self._built or force:
|
||||
if (
|
||||
|
|
@ -59,7 +63,7 @@ class PromptNode(Node):
|
|||
):
|
||||
self.params["input_variables"] = []
|
||||
# Check if it is a ZeroShotPrompt and needs a tool
|
||||
if "ShotPrompt" in self.node_type:
|
||||
if "ShotPrompt" in self.vertex_type:
|
||||
tools = (
|
||||
[tool_node.build() for tool_node in tools]
|
||||
if tools is not None
|
||||
|
|
@ -83,31 +87,31 @@ class PromptNode(Node):
|
|||
return self._built_object
|
||||
|
||||
|
||||
class ChainNode(Node):
|
||||
class ChainVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="chains")
|
||||
|
||||
def build(
|
||||
self,
|
||||
force: bool = False,
|
||||
tools: Optional[Union[List[Node], List[ToolNode]]] = None,
|
||||
tools: Optional[Union[List[Vertex], List[ToolVertex]]] = None,
|
||||
) -> Any:
|
||||
if not self._built or force:
|
||||
# Check if the chain requires a PromptNode
|
||||
for key, value in self.params.items():
|
||||
if isinstance(value, PromptNode):
|
||||
if isinstance(value, PromptVertex):
|
||||
# Build the PromptNode, passing the tools if available
|
||||
self.params[key] = value.build(tools=tools, force=force)
|
||||
|
||||
self._build()
|
||||
|
||||
#! Cannot deepcopy SQLDatabaseChain
|
||||
if self.node_type in ["SQLDatabaseChain"]:
|
||||
if self.vertex_type in ["SQLDatabaseChain"]:
|
||||
return self._built_object
|
||||
return self._built_object
|
||||
|
||||
|
||||
class LLMNode(Node):
|
||||
class LLMVertex(Vertex):
|
||||
built_node_type = None
|
||||
class_built_object = None
|
||||
|
||||
|
|
@ -117,28 +121,28 @@ class LLMNode(Node):
|
|||
def build(self, force: bool = False) -> Any:
|
||||
# LLM is different because some models might take up too much memory
|
||||
# or time to load. So we only load them when we need them.ß
|
||||
if self.node_type == self.built_node_type:
|
||||
if self.vertex_type == self.built_node_type:
|
||||
return self.class_built_object
|
||||
if not self._built or force:
|
||||
self._build()
|
||||
self.built_node_type = self.node_type
|
||||
self.built_node_type = self.vertex_type
|
||||
self.class_built_object = self._built_object
|
||||
# Avoid deepcopying the LLM
|
||||
# that are loaded from a file
|
||||
return self._built_object
|
||||
|
||||
|
||||
class ToolkitNode(Node):
|
||||
class ToolkitVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="toolkits")
|
||||
|
||||
|
||||
class FileToolNode(ToolNode):
|
||||
class FileToolVertex(ToolVertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data)
|
||||
|
||||
|
||||
class WrapperNode(Node):
|
||||
class WrapperVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="wrappers")
|
||||
|
||||
|
|
@ -150,7 +154,7 @@ class WrapperNode(Node):
|
|||
return self._built_object
|
||||
|
||||
|
||||
class DocumentLoaderNode(Node):
|
||||
class DocumentLoaderVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="documentloaders")
|
||||
|
||||
|
|
@ -158,17 +162,17 @@ class DocumentLoaderNode(Node):
|
|||
# This built_object is a list of documents. Maybe we should
|
||||
# show how many documents are in the list?
|
||||
if self._built_object:
|
||||
return f"""{self.node_type}({len(self._built_object)} documents)
|
||||
return f"""{self.vertex_type}({len(self._built_object)} documents)
|
||||
Documents: {self._built_object[:3]}..."""
|
||||
return f"{self.node_type}()"
|
||||
return f"{self.vertex_type}()"
|
||||
|
||||
|
||||
class EmbeddingNode(Node):
|
||||
class EmbeddingVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="embeddings")
|
||||
|
||||
|
||||
class VectorStoreNode(Node):
|
||||
class VectorStoreVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="vectorstores")
|
||||
|
||||
|
|
@ -176,12 +180,12 @@ class VectorStoreNode(Node):
|
|||
return "Vector stores can take time to build. It will build on the first query."
|
||||
|
||||
|
||||
class MemoryNode(Node):
|
||||
class MemoryVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="memory")
|
||||
|
||||
|
||||
class TextSplitterNode(Node):
|
||||
class TextSplitterVertex(Vertex):
|
||||
def __init__(self, data: Dict):
|
||||
super().__init__(data, base_type="textsplitters")
|
||||
|
||||
|
|
@ -189,5 +193,6 @@ class TextSplitterNode(Node):
|
|||
# This built_object is a list of documents. Maybe we should
|
||||
# show how many documents are in the list?
|
||||
if self._built_object:
|
||||
return f"""{self.node_type}({len(self._built_object)} documents)\nDocuments: {self._built_object[:3]}..."""
|
||||
return f"{self.node_type}()"
|
||||
return f"""{self.vertex_type}({len(self._built_object)} documents)
|
||||
\nDocuments: {self._built_object[:3]}..."""
|
||||
return f"{self.vertex_type}()"
|
||||
|
|
@ -11,12 +11,14 @@ from langchain import (
|
|||
text_splitter,
|
||||
)
|
||||
from langchain.agents import agent_toolkits
|
||||
from langchain.chat_models import ChatAnthropic
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
|
||||
from langflow.interface.importing.utils import import_class
|
||||
|
||||
## LLMs
|
||||
llm_type_to_cls_dict = llms.type_to_cls_dict
|
||||
llm_type_to_cls_dict["anthropic-chat"] = ChatAnthropic # type: ignore
|
||||
llm_type_to_cls_dict["openai-chat"] = ChatOpenAI # type: ignore
|
||||
|
||||
## Chains
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from langchain.base_language import BaseLanguageModel
|
|||
from langchain.chains.base import Chain
|
||||
from langchain.chat_models.base import BaseChatModel
|
||||
from langchain.tools import BaseTool
|
||||
from langflow.utils import validate
|
||||
|
||||
|
||||
def import_module(module_path: str) -> Any:
|
||||
|
|
@ -147,3 +148,10 @@ def import_utility(utility: str) -> Any:
|
|||
if utility == "SQLDatabase":
|
||||
return import_class(f"langchain.sql_database.{utility}")
|
||||
return import_class(f"langchain.utilities.{utility}")
|
||||
|
||||
|
||||
def get_function(code):
|
||||
"""Get the function"""
|
||||
function_name = validate.extract_function_name(code)
|
||||
|
||||
return validate.create_function(code, function_name)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from langchain.agents.load_tools import (
|
|||
_LLM_TOOLS,
|
||||
)
|
||||
from langchain.agents.loading import load_agent_from_config
|
||||
from langflow.graph import Graph
|
||||
from langchain.agents.tools import Tool
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.callbacks.base import BaseCallbackManager
|
||||
|
|
@ -20,12 +21,12 @@ from langchain.llms.loading import load_llm_from_config
|
|||
from pydantic import ValidationError
|
||||
|
||||
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
||||
from langflow.interface.importing.utils import import_by_type
|
||||
from langflow.interface.importing.utils import get_function, import_by_type
|
||||
from langflow.interface.run import fix_memory_inputs
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.types import get_type_list
|
||||
from langflow.interface.utils import load_file_into_dict
|
||||
from langflow.utils import util, validate
|
||||
from langflow.utils import util
|
||||
|
||||
|
||||
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
|
||||
|
|
@ -99,11 +100,9 @@ def instantiate_tool(node_type, class_object, params):
|
|||
if node_type == "JsonSpec":
|
||||
params["dict_"] = load_file_into_dict(params.pop("path"))
|
||||
return class_object(**params)
|
||||
elif node_type == "PythonFunction":
|
||||
function_string = params["code"]
|
||||
if isinstance(function_string, str):
|
||||
return validate.eval_function(function_string)
|
||||
raise ValueError("Function should be a string")
|
||||
elif node_type == "PythonFunctionTool":
|
||||
params["func"] = get_function(params.get("code"))
|
||||
return class_object(**params)
|
||||
elif node_type.lower() == "tool":
|
||||
return class_object(**params)
|
||||
return class_object(**params)
|
||||
|
|
@ -164,7 +163,6 @@ def instantiate_utility(node_type, class_object, params):
|
|||
def load_flow_from_json(path: str, build=True):
|
||||
"""Load flow from json file"""
|
||||
# This is done to avoid circular imports
|
||||
from langflow.graph import Graph
|
||||
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
flow_graph = json.load(f)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from langchain.schema import AgentAction
|
|||
|
||||
from langflow.api.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler # type: ignore
|
||||
from langflow.cache.base import compute_dict_hash, load_cache, memoize_dict
|
||||
from langflow.graph.graph import Graph
|
||||
from langflow.graph import Graph
|
||||
from langflow.utils.logger import logger
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ class ToolCreator(LangChainTypeCreator):
|
|||
|
||||
for tool, tool_fcn in ALL_TOOLS_NAMES.items():
|
||||
tool_params = get_tool_params(tool_fcn)
|
||||
tool_name = tool_params.get("name", tool)
|
||||
|
||||
tool_name = tool_params.get("name") or tool
|
||||
|
||||
if tool_name in settings.tools or settings.dev:
|
||||
if tool_name == "JsonSpec":
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ from langchain.agents.load_tools import (
|
|||
from langchain.tools.json.tool import JsonSpec
|
||||
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.interface.tools.custom import PythonFunction
|
||||
from langflow.interface.tools.custom import PythonFunctionTool
|
||||
|
||||
FILE_TOOLS = {"JsonSpec": JsonSpec}
|
||||
CUSTOM_TOOLS = {"Tool": Tool, "PythonFunction": PythonFunction}
|
||||
CUSTOM_TOOLS = {"Tool": Tool, "PythonFunctionTool": PythonFunctionTool}
|
||||
|
||||
OTHER_TOOLS = {tool: import_class(f"langchain.tools.{tool}") for tool in tools.__all__}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
from typing import Callable, Optional
|
||||
from typing import Optional
|
||||
from langflow.interface.importing.utils import get_function
|
||||
|
||||
from pydantic import BaseModel, validator
|
||||
|
||||
from langflow.utils import validate
|
||||
from langchain.agents.tools import Tool
|
||||
|
||||
|
||||
class Function(BaseModel):
|
||||
code: str
|
||||
function: Optional[Callable] = None
|
||||
imports: Optional[str] = None
|
||||
|
||||
# Eval code and store the function
|
||||
|
|
@ -24,14 +25,17 @@ class Function(BaseModel):
|
|||
|
||||
return v
|
||||
|
||||
def get_function(self):
|
||||
"""Get the function"""
|
||||
function_name = validate.extract_function_name(self.code)
|
||||
|
||||
return validate.create_function(self.code, function_name)
|
||||
|
||||
|
||||
class PythonFunction(Function):
|
||||
class PythonFunctionTool(Function, Tool):
|
||||
"""Python function"""
|
||||
|
||||
name: str = "Custom Tool"
|
||||
description: str
|
||||
code: str
|
||||
|
||||
def ___init__(self, name: str, description: str, code: str):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.code = code
|
||||
self.func = get_function(self.code)
|
||||
super().__init__(name=name, description=description, func=self.func)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,9 @@ class FrontendNode(BaseModel):
|
|||
elif name == "ChatOpenAI" and key == "model_name":
|
||||
field.options = constants.CHAT_OPENAI_MODELS
|
||||
field.is_list = True
|
||||
elif (name == "Anthropic" or name == "ChatAnthropic") and key == "model_name":
|
||||
field.options = constants.ANTHROPIC_MODELS
|
||||
field.is_list = True
|
||||
if "api_key" in key and "OpenAI" in str(name):
|
||||
field.display_name = "OpenAI API Key"
|
||||
field.required = False
|
||||
|
|
|
|||
|
|
@ -59,11 +59,33 @@ class ToolNode(FrontendNode):
|
|||
return super().to_dict()
|
||||
|
||||
|
||||
class PythonFunctionNode(FrontendNode):
|
||||
name: str = "PythonFunction"
|
||||
class PythonFunctionToolNode(FrontendNode):
|
||||
name: str = "PythonFunctionTool"
|
||||
template: Template = Template(
|
||||
type_name="python_function",
|
||||
type_name="PythonFunctionTool",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
name="name",
|
||||
advanced=False,
|
||||
),
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
multiline=False,
|
||||
value="",
|
||||
name="description",
|
||||
advanced=False,
|
||||
),
|
||||
TemplateField(
|
||||
field_type="code",
|
||||
required=True,
|
||||
|
|
@ -73,11 +95,11 @@ class PythonFunctionNode(FrontendNode):
|
|||
value=DEFAULT_PYTHON_FUNCTION,
|
||||
name="code",
|
||||
advanced=False,
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
description: str = "Python function to be executed."
|
||||
base_classes: list[str] = ["function"]
|
||||
base_classes: list[str] = ["Tool"]
|
||||
|
||||
def to_dict(self):
|
||||
return super().to_dict()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,20 @@ OPENAI_MODELS = [
|
|||
]
|
||||
CHAT_OPENAI_MODELS = ["gpt-3.5-turbo", "gpt-4", "gpt-4-32k"]
|
||||
|
||||
ANTHROPIC_MODELS = [
|
||||
"claude-v1", # largest model, ideal for a wide range of more complex tasks.
|
||||
"claude-v1-100k", # An enhanced version of claude-v1 with a 100,000 token (roughly 75,000 word) context window.
|
||||
"claude-instant-v1", # A smaller model with far lower latency, sampling at roughly 40 words/sec!
|
||||
"claude-instant-v1-100k", # Like claude-instant-v1 with a 100,000 token context window but retains its performance.
|
||||
# Specific sub-versions of the above models:
|
||||
"claude-v1.3", # Vs claude-v1.2: better instruction-following, code, and non-English dialogue and writing.
|
||||
"claude-v1.3-100k", # An enhanced version of claude-v1.3 with a 100,000 token (roughly 75,000 word) context window.
|
||||
"claude-v1.2", # Vs claude-v1.1: small adv in general helpfulness, instruction following, coding, and other tasks.
|
||||
"claude-v1.0", # An earlier version of claude-v1.
|
||||
"claude-instant-v1.1", # Latest version of claude-instant-v1. Better than claude-instant-v1.0 at most tasks.
|
||||
"claude-instant-v1.1-100k", # Version of claude-instant-v1.1 with a 100K token context window.
|
||||
"claude-instant-v1.0", # An earlier version of claude-instant-v1.
|
||||
]
|
||||
|
||||
DEFAULT_PYTHON_FUNCTION = """
|
||||
def python_function(text: str) -> str:
|
||||
|
|
|
|||
|
|
@ -302,7 +302,9 @@ def format_dict(d, name: Optional[str] = None):
|
|||
elif name == "ChatOpenAI" and key == "model_name":
|
||||
value["options"] = constants.CHAT_OPENAI_MODELS
|
||||
value["list"] = True
|
||||
|
||||
elif (name == "Anthropic" or name == "ChatAnthropic") and key == "model_name":
|
||||
value["options"] = constants.ANTHROPIC_MODELS
|
||||
value["list"] = True
|
||||
return d
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Disclosure } from "@headlessui/react";
|
||||
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { classNames } from "../../utils";
|
||||
import { locationContext } from "../../contexts/locationContext";
|
||||
|
|
@ -13,6 +13,7 @@ export default function ExtraSidebar() {
|
|||
extraNavigation,
|
||||
extraComponent,
|
||||
} = useContext(locationContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
<aside
|
||||
|
|
@ -21,10 +22,8 @@ export default function ExtraSidebar() {
|
|||
} flex-shrink-0 flex overflow-hidden flex-col border-r dark:border-r-gray-700 transition-all duration-500`}
|
||||
>
|
||||
<div className="w-52 dark:bg-gray-800 border dark:border-gray-700 overflow-y-auto scrollbar-hide h-full flex flex-col items-start">
|
||||
<div className="flex pt-1 px-4 justify-between align-middle w-full">
|
||||
<span className="text-gray-900 dark:text-white py-[2px] font-medium ">
|
||||
{extraNavigation.title}
|
||||
</span>
|
||||
<div className="flex px-4 justify-between align-middle w-full">
|
||||
<span className="text-gray-900 dark:text-white py-[2px] font-medium "></span>
|
||||
</div>
|
||||
<div className="flex flex-grow flex-col w-full">
|
||||
{extraNavigation.options ? (
|
||||
|
|
|
|||
9
src/frontend/src/icons/Anthropic/anthropic.svg
Normal file
9
src/frontend/src/icons/Anthropic/anthropic.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="-170.333 113.047 600 67.4" width="600" height="67.4">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
.st0{fill:#1F1F1E;}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st0" d="M -23.533 126.747 L -1.633 126.747 L -1.633 179.347 L 12.367 179.347 L 12.367 126.747 L 34.267 126.747 L 34.267 114.147 L -23.533 114.147 L -23.533 126.747 Z M -48.133 159.747 L -77.433 114.147 L -93.233 114.147 L -93.233 179.247 L -79.733 179.247 L -79.733 133.647 L -50.433 179.347 L -34.633 179.347 L -34.633 114.247 L -48.133 114.247 L -48.133 159.747 Z M 90.067 140.147 L 59.367 140.147 L 59.367 114.147 L 45.367 114.147 L 45.367 179.247 L 59.367 179.247 L 59.367 152.647 L 90.067 152.647 L 90.067 179.247 L 104.067 179.247 L 104.067 114.147 L 90.067 114.147 L 90.067 140.147 Z M -144.333 114.147 L -170.333 179.247 L -155.833 179.247 L -150.533 165.547 L -123.333 165.547 L -118.033 179.247 L -103.533 179.247 L -129.533 114.147 L -144.333 114.147 Z M -145.833 153.547 L -136.933 130.647 L -128.033 153.547 L -145.833 153.547 Z M 219.667 113.047 C 200.867 113.047 187.567 127.047 187.567 146.847 C 187.567 166.447 200.867 180.447 219.667 180.447 C 238.367 180.447 251.567 166.447 251.567 146.847 C 251.567 127.047 238.367 113.047 219.667 113.047 Z M 219.667 167.447 C 208.667 167.447 201.967 159.647 201.967 146.847 C 201.967 133.947 208.667 126.047 219.667 126.047 C 230.567 126.047 237.167 133.847 237.167 146.847 C 237.167 159.547 230.567 167.447 219.667 167.447 Z M 414.767 157.447 C 412.367 163.747 407.467 167.447 400.867 167.447 C 389.867 167.447 383.167 159.647 383.167 146.847 C 383.167 133.947 389.867 126.047 400.867 126.047 C 407.467 126.047 412.267 129.647 414.767 136.047 L 429.567 136.047 C 425.967 122.047 415.067 113.047 400.867 113.047 C 382.067 113.047 368.767 127.047 368.767 146.847 C 368.767 166.447 382.067 180.447 400.867 180.447 C 415.067 180.447 425.967 171.347 429.667 157.447 L 414.767 157.447 Z M 325.867 114.147 L 351.867 179.247 L 366.067 179.247 L 340.067 114.147 L 325.867 114.147 Z M 296.367 114.147 L 264.567 114.147 L 264.567 179.247 L 278.567 179.247 L 278.567 155.647 L 296.467 155.647 C 311.267 155.647 320.267 147.847 320.267 134.847 C 320.267 121.947 311.167 114.147 296.367 114.147 Z M 295.767 143.147 L 278.567 143.147 L 278.567 126.747 L 295.767 126.747 C 302.667 126.747 306.267 129.547 306.267 134.947 C 306.267 140.347 302.667 143.147 295.767 143.147 Z M 176.867 134.047 C 176.867 121.747 167.867 114.247 153.067 114.247 L 121.267 114.247 L 121.267 179.347 L 135.267 179.347 L 135.267 153.847 L 150.767 153.847 L 164.767 179.347 L 180.167 179.347 L 164.667 151.947 C 172.367 148.847 176.867 142.647 176.867 134.047 Z M 135.167 126.747 L 152.367 126.747 C 159.267 126.747 162.867 129.247 162.867 134.047 C 162.867 138.747 159.267 141.347 152.367 141.347 L 135.167 141.347 L 135.167 126.747 Z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
11
src/frontend/src/icons/Anthropic/anthropic_box.svg
Normal file
11
src/frontend/src/icons/Anthropic/anthropic_box.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="-126.9 247.9 207.161 212.728" width="207.161" height="212.728">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
.st0{fill:#1F1F1E;}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st0" d="M 19.9 260.5 L 41.8 260.5 L 41.8 313.1 L 55.8 313.1 L 55.8 260.5 L 77.7 260.5 L 77.7 247.9 L 19.9 247.9 L 19.9 260.5 Z M -4.7 293.5 L -34 247.9 L -49.8 247.9 L -49.8 313 L -36.3 313 L -36.3 267.4 L -7 313.1 L 8.8 313.1 L 8.8 248 L -4.7 248 L -4.7 293.5 Z M -100.9 247.9 L -126.9 313 L -112.4 313 L -107.1 299.3 L -79.9 299.3 L -74.6 313 L -60.1 313 L -86.1 247.9 L -100.9 247.9 Z M -102.4 287.3 L -93.5 264.4 L -84.6 287.3 L -102.4 287.3 Z"></path>
|
||||
<path class="st0" d="M 38.246 437.628 C 35.846 443.928 30.946 447.628 24.346 447.628 C 13.346 447.628 6.646 439.828 6.646 427.028 C 6.646 414.128 13.346 406.228 24.346 406.228 C 30.946 406.228 35.746 409.828 38.246 416.228 L 53.046 416.228 C 49.446 402.228 38.546 393.228 24.346 393.228 C 5.546 393.228 -7.754 407.228 -7.754 427.028 C -7.754 446.628 5.546 460.628 24.346 460.628 C 38.546 460.628 49.446 451.528 53.146 437.628 L 38.246 437.628 Z M -50.654 394.328 L -24.654 459.428 L -10.454 459.428 L -36.454 394.328 L -50.654 394.328 Z M -80.154 394.328 L -111.954 394.328 L -111.954 459.428 L -97.954 459.428 L -97.954 435.828 L -80.054 435.828 C -65.254 435.828 -56.254 428.028 -56.254 415.028 C -56.254 402.128 -65.354 394.328 -80.154 394.328 Z M -80.754 423.328 L -97.954 423.328 L -97.954 406.928 L -80.754 406.928 C -73.854 406.928 -70.254 409.728 -70.254 415.128 C -70.254 420.528 -73.854 423.328 -80.754 423.328 Z"></path>
|
||||
<path class="st0" d="M -81.239 347.704 L -111.939 347.704 L -111.939 321.704 L -125.939 321.704 L -125.939 386.804 L -111.939 386.804 L -111.939 360.204 L -81.239 360.204 L -81.239 386.804 L -67.239 386.804 L -67.239 321.704 L -81.239 321.704 L -81.239 347.704 Z M 48.361 320.604 C 29.561 320.604 16.261 334.604 16.261 354.404 C 16.261 374.004 29.561 388.004 48.361 388.004 C 67.061 388.004 80.261 374.004 80.261 354.404 C 80.261 334.604 67.061 320.604 48.361 320.604 Z M 48.361 375.004 C 37.361 375.004 30.661 367.204 30.661 354.404 C 30.661 341.504 37.361 333.604 48.361 333.604 C 59.261 333.604 65.861 341.404 65.861 354.404 C 65.861 367.104 59.261 375.004 48.361 375.004 Z M 5.561 341.604 C 5.561 329.304 -3.439 321.804 -18.239 321.804 L -50.039 321.804 L -50.039 386.904 L -36.039 386.904 L -36.039 361.404 L -20.539 361.404 L -6.539 386.904 L 8.861 386.904 L -6.639 359.504 C 1.061 356.404 5.561 350.204 5.561 341.604 Z M -36.139 334.304 L -18.939 334.304 C -12.039 334.304 -8.439 336.804 -8.439 341.604 C -8.439 346.304 -12.039 348.904 -18.939 348.904 L -36.139 348.904 L -36.139 334.304 Z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
9
src/frontend/src/icons/Anthropic/index.tsx
Normal file
9
src/frontend/src/icons/Anthropic/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import { ReactComponent as AnthropicSVG } from "./anthropic_box.svg";
|
||||
|
||||
export const AnthropicIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <AnthropicSVG ref={ref} {...props} />;
|
||||
});
|
||||
|
|
@ -15,3 +15,9 @@ code {
|
|||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
/* The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor.
|
||||
The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. */
|
||||
.react-flow__pane {
|
||||
cursor: default;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ import Convert from "ansi-to-html";
|
|||
export default function ChatMessage({
|
||||
chat,
|
||||
lockChat,
|
||||
lastMessage,
|
||||
}: {
|
||||
chat: ChatMessageType;
|
||||
lockChat: boolean;
|
||||
lastMessage: boolean;
|
||||
}) {
|
||||
const convert = new Convert({ newline: true });
|
||||
const [message, setMessage] = useState("");
|
||||
|
|
@ -48,7 +50,7 @@ export default function ChatMessage({
|
|||
"absolute transition-opacity duration-500 scale-150 " +
|
||||
(lockChat ? "opacity-100" : "opacity-0")
|
||||
}
|
||||
src={AiIcon}
|
||||
src={lastMessage ? AiIcon : AiIconStill}
|
||||
/>
|
||||
<img
|
||||
className={
|
||||
|
|
|
|||
|
|
@ -290,7 +290,9 @@ export default function ChatModal({
|
|||
errors.concat(
|
||||
template[t].required &&
|
||||
template[t].show &&
|
||||
(!template[t].value || template[t].value === "") &&
|
||||
(template[t].value === undefined ||
|
||||
template[t].value === null ||
|
||||
template[t].value === "") &&
|
||||
!reactFlowInstance
|
||||
.getEdges()
|
||||
.some(
|
||||
|
|
@ -414,7 +416,12 @@ export default function ChatModal({
|
|||
>
|
||||
{chatHistory.length > 0 ? (
|
||||
chatHistory.map((c, i) => (
|
||||
<ChatMessage lockChat={lockChat} chat={c} key={i} />
|
||||
<ChatMessage
|
||||
lockChat={lockChat}
|
||||
chat={c}
|
||||
lastMessage={chatHistory.length - 1 == i ? true : false}
|
||||
key={i}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="flex flex-col h-full text-center justify-center w-full items-center align-middle">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { DisclosureComponentType } from "../../../../types/components";
|
|||
export default function DisclosureComponent({
|
||||
button: { title, Icon, buttons = [] },
|
||||
children,
|
||||
openDisc,
|
||||
}: DisclosureComponentType) {
|
||||
return (
|
||||
<Disclosure as="div" key={title}>
|
||||
|
|
@ -27,14 +28,14 @@ export default function DisclosureComponent({
|
|||
<div>
|
||||
<ChevronRightIcon
|
||||
className={`${
|
||||
open ? "rotate-90 transform" : ""
|
||||
open || openDisc ? "rotate-90 transform" : ""
|
||||
} h-4 w-4 text-gray-800 dark:text-white`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
<Disclosure.Panel as="div" className="-mt-px">
|
||||
<Disclosure.Panel as="div" className="-mt-px" static={openDisc}>
|
||||
{children}
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import { Bars2Icon } from "@heroicons/react/24/outline";
|
||||
import DisclosureComponent from "../DisclosureComponent";
|
||||
import { nodeColors, nodeIcons, nodeNames } from "../../../../utils";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
classNames,
|
||||
nodeColors,
|
||||
nodeIcons,
|
||||
nodeNames,
|
||||
} from "../../../../utils";
|
||||
import { useContext, useEffect, useState, useRef } from "react";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
import { APIClassType, APIObjectType } from "../../../../types/api";
|
||||
import TooltipReact from "../../../../components/ReactTooltipComponent";
|
||||
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
export default function ExtraSidebar() {
|
||||
const { data } = useContext(typesContext);
|
||||
const [dataFilter, setFilterData] = useState(data);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
function onDragStart(
|
||||
event: React.DragEvent<any>,
|
||||
|
|
@ -24,66 +32,104 @@ export default function ExtraSidebar() {
|
|||
event.dataTransfer.setData("json", JSON.stringify(data));
|
||||
}
|
||||
|
||||
function handleSearchInput(e: string) {
|
||||
setFilterData((_) => {
|
||||
let ret = {};
|
||||
Object.keys(data).forEach((d: keyof APIObjectType, i) => {
|
||||
ret[d] = {};
|
||||
let keys = Object.keys(data[d]).filter((nd) =>
|
||||
nd.toLowerCase().includes(e.toLowerCase())
|
||||
);
|
||||
keys.forEach((element) => {
|
||||
ret[d][element] = data[d][element];
|
||||
});
|
||||
});
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-1 w-full">
|
||||
{Object.keys(data)
|
||||
.sort()
|
||||
.map((d: keyof APIObjectType, i) => (
|
||||
<DisclosureComponent
|
||||
key={i}
|
||||
button={{
|
||||
title: nodeNames[d] ?? nodeNames.unknown,
|
||||
Icon: nodeIcons[d] ?? nodeIcons.unknown,
|
||||
}}
|
||||
>
|
||||
<div className="p-2 flex flex-col gap-2">
|
||||
{Object.keys(data[d])
|
||||
.sort()
|
||||
.map((t: string, k) => (
|
||||
<TooltipReact
|
||||
selector={t}
|
||||
htmlContent={t}
|
||||
position="right"
|
||||
delayShow={1500}
|
||||
key={k}
|
||||
>
|
||||
<div key={k} data-tooltip-id={t}>
|
||||
<div
|
||||
draggable
|
||||
className={" cursor-grab border-l-8 rounded-l-md"}
|
||||
style={{
|
||||
borderLeftColor: nodeColors[d] ?? nodeColors.unknown,
|
||||
}}
|
||||
onDragStart={(event) =>
|
||||
onDragStart(event, {
|
||||
type: t,
|
||||
node: data[d][t],
|
||||
})
|
||||
}
|
||||
onDragEnd={() => {
|
||||
document.body.removeChild(
|
||||
document.getElementsByClassName(
|
||||
"cursor-grabbing"
|
||||
)[0]
|
||||
);
|
||||
}}
|
||||
<>
|
||||
<div className="relative mt-2 flex items-center mb-2 mx-2">
|
||||
<input
|
||||
type="text"
|
||||
name="search"
|
||||
id="search"
|
||||
placeholder="Search nodes"
|
||||
className="dark:text-white focus:outline-none block w-full rounded-md py-1.5 ps-3 pr-9 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 sm:text-sm sm:leading-6 dark:ring-0 dark:bg-[#2d3747] dark:focus:outline-none"
|
||||
onChange={(e) => {
|
||||
handleSearchInput(e.target.value);
|
||||
setSearch(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex py-1.5 pr-3 items-center">
|
||||
<MagnifyingGlassIcon className="h-5 w-5 dark:text-white"></MagnifyingGlassIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-1 w-full">
|
||||
{Object.keys(dataFilter)
|
||||
.sort()
|
||||
.map((d: keyof APIObjectType, i) =>
|
||||
Object.keys(dataFilter[d]).length > 0 ? (
|
||||
<DisclosureComponent
|
||||
openDisc={search.length == 0 ? false : true}
|
||||
key={i}
|
||||
button={{
|
||||
title: nodeNames[d] ?? nodeNames.unknown,
|
||||
Icon: nodeIcons[d] ?? nodeIcons.unknown,
|
||||
}}
|
||||
>
|
||||
<div className="p-2 flex flex-col gap-2">
|
||||
{Object.keys(dataFilter[d])
|
||||
.sort()
|
||||
.map((t: string, k) => (
|
||||
<TooltipReact
|
||||
selector={t}
|
||||
htmlContent={t}
|
||||
position="right"
|
||||
delayShow={1500}
|
||||
key={k}
|
||||
>
|
||||
<div className="flex w-full justify-between text-sm px-3 py-1 bg-white dark:bg-gray-800 items-center border-dashed border-gray-400 dark:border-gray-600 border-l-0 rounded-md rounded-l-none border">
|
||||
<span className="text-black dark:text-white w-36 pr-1 truncate text-xs">
|
||||
{t}
|
||||
</span>
|
||||
<Bars2Icon className="w-4 h-6 text-gray-400 dark:text-gray-600" />
|
||||
<div key={k} data-tooltip-id={t}>
|
||||
<div
|
||||
draggable
|
||||
className={" cursor-grab border-l-8 rounded-l-md"}
|
||||
style={{
|
||||
borderLeftColor:
|
||||
nodeColors[d] ?? nodeColors.unknown,
|
||||
}}
|
||||
onDragStart={(event) =>
|
||||
onDragStart(event, {
|
||||
type: t,
|
||||
node: data[d][t],
|
||||
})
|
||||
}
|
||||
onDragEnd={() => {
|
||||
document.body.removeChild(
|
||||
document.getElementsByClassName(
|
||||
"cursor-grabbing"
|
||||
)[0]
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full justify-between text-sm px-3 py-1 bg-white dark:bg-gray-800 items-center border-dashed border-gray-400 dark:border-gray-600 border-l-0 rounded-md rounded-l-none border">
|
||||
<span className="text-black dark:text-white w-36 pr-1 truncate text-xs">
|
||||
{t}
|
||||
</span>
|
||||
<Bars2Icon className="w-4 h-6 text-gray-400 dark:text-gray-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipReact>
|
||||
))}
|
||||
{Object.keys(data[d]).length === 0 && (
|
||||
<div className="text-gray-400 text-center">Coming soon</div>
|
||||
)}
|
||||
</div>
|
||||
</DisclosureComponent>
|
||||
))}
|
||||
</div>
|
||||
</TooltipReact>
|
||||
))}
|
||||
</div>
|
||||
</DisclosureComponent>
|
||||
) : (
|
||||
<div key={i}></div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ export type FileComponentType = {
|
|||
|
||||
export type DisclosureComponentType = {
|
||||
children: ReactNode;
|
||||
openDisc: boolean;
|
||||
button: {
|
||||
title: string;
|
||||
Icon: ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { FlowType, NodeType } from "./types/flow";
|
|||
import { APITemplateType, TemplateVariableType } from "./types/api";
|
||||
import _ from "lodash";
|
||||
import { ChromaIcon } from "./icons/ChromaIcon";
|
||||
import { AnthropicIcon } from "./icons/Anthropic";
|
||||
import { AirbyteIcon } from "./icons/Airbyte";
|
||||
import { AzIcon } from "./icons/AzLogo";
|
||||
import { BingIcon } from "./icons/Bing";
|
||||
|
|
@ -153,6 +154,8 @@ export const nodeIcons: {
|
|||
AirbyteJSONLoader: AirbyteIcon,
|
||||
// SerpAPIWrapper: SerperIcon,
|
||||
// AZLyricsLoader: AzIcon,
|
||||
Anthropic: AnthropicIcon,
|
||||
ChatAnthropic: AnthropicIcon,
|
||||
BingSearchAPIWrapper: BingIcon,
|
||||
BingSearchRun: BingIcon,
|
||||
Cohere: CohereIcon,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from pathlib import Path
|
|||
from typing import AsyncGenerator
|
||||
from langflow.api.database import get_session
|
||||
|
||||
from langflow.graph.graph.base import Graph
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from httpx import AsyncClient
|
||||
|
|
@ -49,7 +50,6 @@ def client():
|
|||
|
||||
def get_graph(_type="basic"):
|
||||
"""Get a graph from a json file"""
|
||||
from langflow.graph.graph import Graph
|
||||
|
||||
if _type == "basic":
|
||||
path = pytest.BASIC_EXAMPLE_PATH
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@
|
|||
"y": 136.29836646158452
|
||||
},
|
||||
"data": {
|
||||
"type": "PythonFunction",
|
||||
"type": "PythonFunctionTool",
|
||||
"node": {
|
||||
"template": {
|
||||
"code": {
|
||||
|
|
@ -210,6 +210,26 @@
|
|||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"description": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "My description",
|
||||
"name": "description",
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"name": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "My Tool",
|
||||
"name": "name",
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"_type": "python_function"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
|
|
|
|||
|
|
@ -1,16 +1,23 @@
|
|||
# Test this:
|
||||
from langflow.interface.importing.utils import get_function
|
||||
import pytest
|
||||
from langflow.interface.tools.custom import PythonFunction
|
||||
from langflow.interface.tools.custom import PythonFunctionTool
|
||||
from langflow.utils import constants
|
||||
|
||||
|
||||
def test_python_function():
|
||||
"""Test Python function"""
|
||||
func = PythonFunction(code=constants.DEFAULT_PYTHON_FUNCTION)
|
||||
assert func.get_function()("text") == "text"
|
||||
code = constants.DEFAULT_PYTHON_FUNCTION
|
||||
func = get_function(code)
|
||||
func = PythonFunctionTool(name="Test", description="Testing", code=code, func=func)
|
||||
assert func("text") == "text"
|
||||
# the tool decorator should raise an error if
|
||||
# the function is not str -> str
|
||||
|
||||
# This raises ValidationError
|
||||
with pytest.raises(SyntaxError):
|
||||
func = PythonFunction(code=pytest.CODE_WITH_SYNTAX_ERROR)
|
||||
code = pytest.CODE_WITH_SYNTAX_ERROR
|
||||
func = get_function(code)
|
||||
func = PythonFunctionTool(
|
||||
name="Test", description="Testing", code=code, func=func
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
from typing import Type, Union
|
||||
from langflow.graph.edge.base import Edge
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
|
||||
import pytest
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.llms.fake import FakeListLLM
|
||||
from langflow.graph import Edge, Graph, Node
|
||||
from langflow.graph.nodes import (
|
||||
AgentNode,
|
||||
ChainNode,
|
||||
FileToolNode,
|
||||
LLMNode,
|
||||
PromptNode,
|
||||
ToolkitNode,
|
||||
ToolNode,
|
||||
WrapperNode,
|
||||
from langflow.graph import Graph
|
||||
from langflow.graph.vertex.types import (
|
||||
AgentVertex,
|
||||
ChainVertex,
|
||||
FileToolVertex,
|
||||
LLMVertex,
|
||||
PromptVertex,
|
||||
ToolkitVertex,
|
||||
ToolVertex,
|
||||
WrapperVertex,
|
||||
)
|
||||
from langflow.interface.run import get_result_and_thought
|
||||
from langflow.utils.payload import get_root_node
|
||||
|
|
@ -23,7 +25,7 @@ from langflow.utils.payload import get_root_node
|
|||
# BASIC_EXAMPLE_PATH, COMPLEX_EXAMPLE_PATH, OPENAPI_EXAMPLE_PATH
|
||||
|
||||
|
||||
def get_node_by_type(graph, node_type: Type[Node]) -> Union[Node, None]:
|
||||
def get_node_by_type(graph, node_type: Type[Vertex]) -> Union[Vertex, None]:
|
||||
"""Get a node by type"""
|
||||
return next((node for node in graph.nodes if isinstance(node, node_type)), None)
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ def test_graph_structure(basic_graph):
|
|||
assert len(basic_graph.nodes) > 0
|
||||
assert len(basic_graph.edges) > 0
|
||||
for node in basic_graph.nodes:
|
||||
assert isinstance(node, Node)
|
||||
assert isinstance(node, Vertex)
|
||||
for edge in basic_graph.edges:
|
||||
assert isinstance(edge, Edge)
|
||||
assert edge.source in basic_graph.nodes
|
||||
|
|
@ -156,14 +158,16 @@ def test_get_node_neighbors_complex(complex_graph):
|
|||
tool_neighbors = complex_graph.get_nodes_with_target(tool)
|
||||
assert tool_neighbors is not None
|
||||
# Check if there is a PythonFunction in the tool's neighbors
|
||||
assert any("PythonFunction" in neighbor.data["type"] for neighbor in tool_neighbors)
|
||||
assert any(
|
||||
"PythonFunctionTool" in neighbor.data["type"] for neighbor in tool_neighbors
|
||||
)
|
||||
|
||||
|
||||
def test_get_node(basic_graph):
|
||||
"""Test getting a single node"""
|
||||
node_id = basic_graph.nodes[0].id
|
||||
node = basic_graph.get_node(node_id)
|
||||
assert isinstance(node, Node)
|
||||
assert isinstance(node, Vertex)
|
||||
assert node.id == node_id
|
||||
|
||||
|
||||
|
|
@ -172,7 +176,7 @@ def test_build_nodes(basic_graph):
|
|||
|
||||
assert len(basic_graph.nodes) == len(basic_graph._nodes)
|
||||
for node in basic_graph.nodes:
|
||||
assert isinstance(node, Node)
|
||||
assert isinstance(node, Vertex)
|
||||
|
||||
|
||||
def test_build_edges(basic_graph):
|
||||
|
|
@ -180,8 +184,8 @@ def test_build_edges(basic_graph):
|
|||
assert len(basic_graph.edges) == len(basic_graph._edges)
|
||||
for edge in basic_graph.edges:
|
||||
assert isinstance(edge, Edge)
|
||||
assert isinstance(edge.source, Node)
|
||||
assert isinstance(edge.target, Node)
|
||||
assert isinstance(edge.source, Vertex)
|
||||
assert isinstance(edge.target, Vertex)
|
||||
|
||||
|
||||
def test_get_root_node(basic_graph, complex_graph):
|
||||
|
|
@ -189,13 +193,13 @@ def test_get_root_node(basic_graph, complex_graph):
|
|||
assert isinstance(basic_graph, Graph)
|
||||
root = get_root_node(basic_graph)
|
||||
assert root is not None
|
||||
assert isinstance(root, Node)
|
||||
assert isinstance(root, Vertex)
|
||||
assert root.data["type"] == "TimeTravelGuideChain"
|
||||
# For complex example, the root node is a ZeroShotAgent too
|
||||
assert isinstance(complex_graph, Graph)
|
||||
root = get_root_node(complex_graph)
|
||||
assert root is not None
|
||||
assert isinstance(root, Node)
|
||||
assert isinstance(root, Vertex)
|
||||
assert root.data["type"] == "ZeroShotAgent"
|
||||
|
||||
|
||||
|
|
@ -255,14 +259,14 @@ def assert_agent_was_built(graph):
|
|||
|
||||
|
||||
def test_agent_node_build(complex_graph):
|
||||
agent_node = get_node_by_type(complex_graph, AgentNode)
|
||||
agent_node = get_node_by_type(complex_graph, AgentVertex)
|
||||
assert agent_node is not None
|
||||
built_object = agent_node.build()
|
||||
assert built_object is not None
|
||||
|
||||
|
||||
def test_tool_node_build(complex_graph):
|
||||
tool_node = get_node_by_type(complex_graph, ToolNode)
|
||||
tool_node = get_node_by_type(complex_graph, ToolVertex)
|
||||
assert tool_node is not None
|
||||
built_object = tool_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -270,7 +274,7 @@ def test_tool_node_build(complex_graph):
|
|||
|
||||
|
||||
def test_chain_node_build(complex_graph):
|
||||
chain_node = get_node_by_type(complex_graph, ChainNode)
|
||||
chain_node = get_node_by_type(complex_graph, ChainVertex)
|
||||
assert chain_node is not None
|
||||
built_object = chain_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -278,7 +282,7 @@ def test_chain_node_build(complex_graph):
|
|||
|
||||
|
||||
def test_prompt_node_build(complex_graph):
|
||||
prompt_node = get_node_by_type(complex_graph, PromptNode)
|
||||
prompt_node = get_node_by_type(complex_graph, PromptVertex)
|
||||
assert prompt_node is not None
|
||||
built_object = prompt_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -286,7 +290,7 @@ def test_prompt_node_build(complex_graph):
|
|||
|
||||
|
||||
def test_llm_node_build(basic_graph):
|
||||
llm_node = get_node_by_type(basic_graph, LLMNode)
|
||||
llm_node = get_node_by_type(basic_graph, LLMVertex)
|
||||
assert llm_node is not None
|
||||
built_object = llm_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -294,7 +298,7 @@ def test_llm_node_build(basic_graph):
|
|||
|
||||
|
||||
def test_toolkit_node_build(openapi_graph):
|
||||
toolkit_node = get_node_by_type(openapi_graph, ToolkitNode)
|
||||
toolkit_node = get_node_by_type(openapi_graph, ToolkitVertex)
|
||||
assert toolkit_node is not None
|
||||
built_object = toolkit_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -302,7 +306,7 @@ def test_toolkit_node_build(openapi_graph):
|
|||
|
||||
|
||||
def test_file_tool_node_build(openapi_graph):
|
||||
file_tool_node = get_node_by_type(openapi_graph, FileToolNode)
|
||||
file_tool_node = get_node_by_type(openapi_graph, FileToolVertex)
|
||||
assert file_tool_node is not None
|
||||
built_object = file_tool_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -310,7 +314,7 @@ def test_file_tool_node_build(openapi_graph):
|
|||
|
||||
|
||||
def test_wrapper_node_build(openapi_graph):
|
||||
wrapper_node = get_node_by_type(openapi_graph, WrapperNode)
|
||||
wrapper_node = get_node_by_type(openapi_graph, WrapperVertex)
|
||||
assert wrapper_node is not None
|
||||
built_object = wrapper_node.build()
|
||||
assert built_object is not None
|
||||
|
|
@ -325,7 +329,7 @@ def test_get_result_and_thought(basic_graph):
|
|||
message = "Hello"
|
||||
# Find the node that is an LLMNode and change the
|
||||
# _built_object to a FakeListLLM
|
||||
llm_node = get_node_by_type(basic_graph, LLMNode)
|
||||
llm_node = get_node_by_type(basic_graph, LLMVertex)
|
||||
assert llm_node is not None
|
||||
llm_node._built_object = FakeListLLM(responses=responses)
|
||||
llm_node._built = True
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue