Merge branch 'dev' into docs-playground

This commit is contained in:
Mendon Kissling 2024-05-17 13:40:15 -04:00
commit e24f14cadc
30 changed files with 635 additions and 799 deletions

View file

@ -4,6 +4,19 @@
# Do not commit .env file to git # Do not commit .env file to git
# Do not change .env.example file # Do not change .env.example file
# Config directory
# Directory where files, logs and database will be stored
# Example: LANGFLOW_CONFIG_DIR=~/.langflow
LANGFLOW_CONFIG_DIR=
# Save database in the config directory
# Values: true, false
# If false, the database will be saved in Langflow's root directory
# This means that the database will be deleted when Langflow is uninstalled
# and that the database will not be shared between different virtual environments
# Example: LANGFLOW_SAVE_DB_IN_CONFIG_DIR=true
LANGFLOW_SAVE_DB_IN_CONFIG_DIR=
# Database URL # Database URL
# Postgres example: LANGFLOW_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/langflow # Postgres example: LANGFLOW_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/langflow
# SQLite example: # SQLite example:
@ -56,7 +69,6 @@ LANGFLOW_REMOVE_API_KEYS=
# LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600) # LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600)
LANGFLOW_CACHE_TYPE= LANGFLOW_CACHE_TYPE=
# Set AUTO_LOGIN to false if you want to disable auto login # Set AUTO_LOGIN to false if you want to disable auto login
# and use the login form to login. LANGFLOW_SUPERUSER and LANGFLOW_SUPERUSER_PASSWORD # and use the login form to login. LANGFLOW_SUPERUSER and LANGFLOW_SUPERUSER_PASSWORD
# must be set if AUTO_LOGIN is set to false # must be set if AUTO_LOGIN is set to false

View file

@ -10,7 +10,7 @@
# PYTHON-BASE # PYTHON-BASE
# Sets up all our shared environment variables # Sets up all our shared environment variables
################################ ################################
FROM python:3.10-slim as python-base FROM python:3.12-slim as python-base
# python # python
ENV PYTHONUNBUFFERED=1 \ ENV PYTHONUNBUFFERED=1 \
@ -47,7 +47,7 @@ ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"
# Used to build deps + create our virtual environment # Used to build deps + create our virtual environment
################################ ################################
FROM python-base as builder-base FROM python-base as builder-base
RUN
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends -y \ && apt-get install --no-install-recommends -y \
# deps for installing poetry # deps for installing poetry
@ -55,7 +55,12 @@ RUN apt-get update \
# deps for building python deps # deps for building python deps
build-essential \ build-essential \
# npm # npm
npm npm \
# gcc
gcc \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Now we need to copy the entire project into the image # Now we need to copy the entire project into the image
@ -70,15 +75,12 @@ RUN --mount=type=cache,target=/root/.cache \
RUN python -m pip install requests && cd ./scripts && python update_dependencies.py RUN python -m pip install requests && cd ./scripts && python update_dependencies.py
RUN $POETRY_HOME/bin/poetry lock RUN $POETRY_HOME/bin/poetry lock
RUN $POETRY_HOME/bin/poetry build RUN $POETRY_HOME/bin/poetry build
# Final stage for the application
FROM python-base as final
# Copy virtual environment and built .tar.gz from builder base # Copy virtual environment and built .tar.gz from builder base
RUN useradd -m -u 1000 user RUN useradd -m -u 1000 user
COPY --from=builder-base /app/dist/*.tar.gz ./
# Install the package from the .tar.gz # Install the package from the .tar.gz
RUN python -m pip install *.tar.gz --user RUN python -m pip install /app/dist/*.tar.gz --user
WORKDIR /app
ENTRYPOINT ["python", "-m", "langflow", "run"] ENTRYPOINT ["python", "-m", "langflow", "run"]
CMD ["--host", "0.0.0.0", "--port", "7860"] CMD ["--host", "0.0.0.0", "--port", "7860"]

View file

@ -10,7 +10,7 @@
# PYTHON-BASE # PYTHON-BASE
# Sets up all our shared environment variables # Sets up all our shared environment variables
################################ ################################
FROM python:3.10-slim as python-base FROM python:3.12-slim as python-base
# python # python
ENV PYTHONUNBUFFERED=1 \ ENV PYTHONUNBUFFERED=1 \
@ -55,6 +55,8 @@ RUN apt-get update \
build-essential \ build-essential \
# npm # npm
npm \ npm \
# gcc
gcc \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@ -78,15 +80,11 @@ RUN cp -r src/frontend/build src/backend/base/langflow/frontend
RUN rm -rf src/backend/base/dist RUN rm -rf src/backend/base/dist
RUN cd src/backend/base && $POETRY_HOME/bin/poetry build --format sdist RUN cd src/backend/base && $POETRY_HOME/bin/poetry build --format sdist
# Final stage for the application
FROM python-base as final
# Copy virtual environment and built .tar.gz from builder base # Copy virtual environment and built .tar.gz from builder base
RUN useradd -m -u 1000 user RUN useradd -m -u 1000 user
COPY --from=builder-base /app/src/backend/base/dist/*.tar.gz ./
# Install the package from the .tar.gz # Install the package from the .tar.gz
RUN pip install *.tar.gz --user RUN python -m pip install /app/dist/*.tar.gz --user
WORKDIR /app
ENTRYPOINT ["python", "-m", "langflow", "run"] ENTRYPOINT ["python", "-m", "langflow", "run"]
CMD ["--host", "0.0.0.0", "--port", "7860"] CMD ["--host", "0.0.0.0", "--port", "7860"]

230
poetry.lock generated
View file

@ -469,17 +469,17 @@ files = [
[[package]] [[package]]
name = "boto3" name = "boto3"
version = "1.34.103" version = "1.34.105"
description = "The AWS SDK for Python" description = "The AWS SDK for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "boto3-1.34.103-py3-none-any.whl", hash = "sha256:59b6499f1bb423dd99de6566a20d0a7cf1a5476824be3a792290fd86600e8365"}, {file = "boto3-1.34.105-py3-none-any.whl", hash = "sha256:b633e8fbf7145bdb995ce68a27d096bb89fd393185b0e773418d81cd78db5a03"},
{file = "boto3-1.34.103.tar.gz", hash = "sha256:58d097241f3895c4a4c80c9e606689c6e06d77f55f9f53a4cc02dee7e03938b9"}, {file = "boto3-1.34.105.tar.gz", hash = "sha256:f2c11635be0de7b7c06eb606ece1add125e02d6ed521592294a0a21af09af135"},
] ]
[package.dependencies] [package.dependencies]
botocore = ">=1.34.103,<1.35.0" botocore = ">=1.34.105,<1.35.0"
jmespath = ">=0.7.1,<2.0.0" jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0" s3transfer = ">=0.10.0,<0.11.0"
@ -488,13 +488,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]] [[package]]
name = "botocore" name = "botocore"
version = "1.34.103" version = "1.34.105"
description = "Low-level, data-driven core of boto 3." description = "Low-level, data-driven core of boto 3."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "botocore-1.34.103-py3-none-any.whl", hash = "sha256:0330d139f18f78d38127e65361859e24ebd6a8bcba184f903c01bb999a3fa431"}, {file = "botocore-1.34.105-py3-none-any.whl", hash = "sha256:a459d060b541beecb50681e6e8a39313cca981e146a59ba7c5229d62f631a016"},
{file = "botocore-1.34.103.tar.gz", hash = "sha256:5f07e2c7302c0a9f469dcd08b4ddac152e9f5888b12220242c20056255010939"}, {file = "botocore-1.34.105.tar.gz", hash = "sha256:727d5d3e800ac8b705fca6e19b6fefa1e728a81d62a712df9bd32ed0117c740b"},
] ]
[package.dependencies] [package.dependencies]
@ -2476,13 +2476,13 @@ examples = ["oauth2"]
[[package]] [[package]]
name = "google-ai-generativelanguage" name = "google-ai-generativelanguage"
version = "0.6.2" version = "0.6.3"
description = "Google Ai Generativelanguage API client library" description = "Google Ai Generativelanguage API client library"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "google-ai-generativelanguage-0.6.2.tar.gz", hash = "sha256:308791ac3b9dad015b359172970739aa3753dd542142a416d07f9fa047e22386"}, {file = "google-ai-generativelanguage-0.6.3.tar.gz", hash = "sha256:10a11f1e1bb8470ff50030c1acd729b3aba7a29ade2c30cf1d1c917291366c67"},
{file = "google_ai_generativelanguage-0.6.2-py3-none-any.whl", hash = "sha256:bf84c34c641570d7e8a1f2e6901e6771af1438f2ee8307d1801fd43585f9b1c6"}, {file = "google_ai_generativelanguage-0.6.3-py3-none-any.whl", hash = "sha256:55a6698f6c9cbbfde5f9cd288073b6941dd9e3e6dc2176dfa3197f9a4c489895"},
] ]
[package.dependencies] [package.dependencies]
@ -2796,16 +2796,16 @@ testing = ["pytest"]
[[package]] [[package]]
name = "google-generativeai" name = "google-generativeai"
version = "0.5.2" version = "0.5.3"
description = "Google Generative AI High level API client library and tools." description = "Google Generative AI High level API client library and tools."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "google_generativeai-0.5.2-py3-none-any.whl", hash = "sha256:56f39485a0a673c93c21ec31c17809cc6a964193fb77b7ce809ad15d0dd72d7b"}, {file = "google_generativeai-0.5.3-py3-none-any.whl", hash = "sha256:a74509ee219601c74c0561eb4e1c9af6a88594c7dd098d30a18c6592afe62bd9"},
] ]
[package.dependencies] [package.dependencies]
google-ai-generativelanguage = "0.6.2" google-ai-generativelanguage = "0.6.3"
google-api-core = "*" google-api-core = "*"
google-api-python-client = "*" google-api-python-client = "*"
google-auth = ">=2.15.0" google-auth = ">=2.15.0"
@ -4234,7 +4234,7 @@ types-requests = ">=2.31.0.2,<3.0.0.0"
[[package]] [[package]]
name = "langflow-base" name = "langflow-base"
version = "0.0.43" version = "0.0.44"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
optional = false optional = false
python-versions = ">=3.10,<3.13" python-versions = ">=3.10,<3.13"
@ -4346,13 +4346,13 @@ regex = ["regex"]
[[package]] [[package]]
name = "litellm" name = "litellm"
version = "1.37.5" version = "1.37.9"
description = "Library to easily interface with LLM API providers" description = "Library to easily interface with LLM API providers"
optional = false optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
files = [ files = [
{file = "litellm-1.37.5-py3-none-any.whl", hash = "sha256:a444dad4079d3d4c49037fe37581cd04b2135e674e9e9d1cfdbda32facd546ec"}, {file = "litellm-1.37.9-py3-none-any.whl", hash = "sha256:f5e96df4e28e302a5df4e8f5599ac35ffbeebbacf4d68f4c609155e24ba8fa03"},
{file = "litellm-1.37.5.tar.gz", hash = "sha256:22d7292d2952d82992ebebc3b7dfa1a97393f603ce652f3223f2742123ba7f2b"}, {file = "litellm-1.37.9.tar.gz", hash = "sha256:2a2fecacde1e3a414b3865c99e5be3b5cffd21433e2648f660116f1ac9333294"},
] ]
[package.dependencies] [package.dependencies]
@ -4368,7 +4368,7 @@ tokenizers = "*"
[package.extras] [package.extras]
extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"]
proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.109.1,<0.110.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"]
[[package]] [[package]]
name = "llama-cpp-python" name = "llama-cpp-python"
@ -4394,13 +4394,13 @@ test = ["httpx (>=0.24.1)", "pytest (>=7.4.0)", "scipy (>=1.10)"]
[[package]] [[package]]
name = "llama-index" name = "llama-index"
version = "0.10.36" version = "0.10.37"
description = "Interface between LLMs and your data" description = "Interface between LLMs and your data"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "llama_index-0.10.36-py3-none-any.whl", hash = "sha256:e57779f332323b00576cf9e8fee0ab5b978aaf35902288691da01a7839b99e58"}, {file = "llama_index-0.10.37-py3-none-any.whl", hash = "sha256:da8871c5c8e5d038e56c0e5cb8c18a81ddc4117bf403bace95b4cec212f88fb9"},
{file = "llama_index-0.10.36.tar.gz", hash = "sha256:275309a2317e9279b296e552c334e566c4f011223f6ed39e342f5264a05c4d9a"}, {file = "llama_index-0.10.37.tar.gz", hash = "sha256:d5057fd609e2423e75a4695242ab030d1647e4f07cb46faf9476ab504005f033"},
] ]
[package.dependencies] [package.dependencies]
@ -4419,13 +4419,13 @@ llama-index-readers-llama-parse = ">=0.1.2,<0.2.0"
[[package]] [[package]]
name = "llama-index-agent-openai" name = "llama-index-agent-openai"
version = "0.2.4" version = "0.2.5"
description = "llama-index agent openai integration" description = "llama-index agent openai integration"
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "llama_index_agent_openai-0.2.4-py3-none-any.whl", hash = "sha256:b05eb7f0331d40a7a2bcaabaa84c9c7ebe6837a72038d03cbb71c083a4301a81"}, {file = "llama_index_agent_openai-0.2.5-py3-none-any.whl", hash = "sha256:67536bb104b24734f79324207034d948a2ca7e4cc20dd60cf05d6eeb4b12a586"},
{file = "llama_index_agent_openai-0.2.4.tar.gz", hash = "sha256:cd4a58f8bf233728ceda554cbb34de56a2b6bbbbff6ce801c3f8ff0c8280bf55"}, {file = "llama_index_agent_openai-0.2.5.tar.gz", hash = "sha256:45f4cc670d037a8a67f541d3a4d095f7f61caff6ed2c25702441eb1116d4b495"},
] ]
[package.dependencies] [package.dependencies]
@ -4659,13 +4659,13 @@ llama-parse = ">=0.4.0,<0.5.0"
[[package]] [[package]]
name = "llama-parse" name = "llama-parse"
version = "0.4.2" version = "0.4.3"
description = "Parse files into RAG-Optimized formats." description = "Parse files into RAG-Optimized formats."
optional = false optional = false
python-versions = "<4.0,>=3.8.1" python-versions = "<4.0,>=3.8.1"
files = [ files = [
{file = "llama_parse-0.4.2-py3-none-any.whl", hash = "sha256:5ce0390141f216dcd88c1123fea7f2a4f561d177f791a97217a3db3509dec4ff"}, {file = "llama_parse-0.4.3-py3-none-any.whl", hash = "sha256:c48c53a3080daeede293df620dddb1f381e084c31ee2dd44dce3f8615df723e8"},
{file = "llama_parse-0.4.2.tar.gz", hash = "sha256:fa04c09730b102155f6505de9cf91998c86d334581f0f12597c5eb47ca5db859"}, {file = "llama_parse-0.4.3.tar.gz", hash = "sha256:01836147b5238873b24a7dd41c5ab942b01b09b92d75570f30cf2861c084a0eb"},
] ]
[package.dependencies] [package.dependencies]
@ -4886,13 +4886,13 @@ source = ["Cython (>=3.0.10)"]
[[package]] [[package]]
name = "mako" name = "mako"
version = "1.3.3" version = "1.3.5"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages." description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40"}, {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"},
{file = "Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73"}, {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"},
] ]
[package.dependencies] [package.dependencies]
@ -5864,13 +5864,13 @@ sympy = "*"
[[package]] [[package]]
name = "openai" name = "openai"
version = "1.28.2" version = "1.30.1"
description = "The official Python library for the openai API" description = "The official Python library for the openai API"
optional = false optional = false
python-versions = ">=3.7.1" python-versions = ">=3.7.1"
files = [ files = [
{file = "openai-1.28.2-py3-none-any.whl", hash = "sha256:2b2ebe032080b53f0a91ebe5f45462d3a5b504d82a6bc5146c750653a47be587"}, {file = "openai-1.30.1-py3-none-any.whl", hash = "sha256:c9fb3c3545c118bbce8deb824397b9433a66d0d0ede6a96f7009c95b76de4a46"},
{file = "openai-1.28.2.tar.gz", hash = "sha256:a0cae5785e2d1e9ae0732970fc0555da13a5a7e08d0a339fb670525d3b5e0e1a"}, {file = "openai-1.30.1.tar.gz", hash = "sha256:4f85190e577cba0b066e1950b8eb9b11d25bc7ebcc43a86b326ce1bfa564ec74"},
] ]
[package.dependencies] [package.dependencies]
@ -6230,13 +6230,13 @@ xml = ["lxml (>=4.9.2)"]
[[package]] [[package]]
name = "pandas-stubs" name = "pandas-stubs"
version = "2.2.1.240316" version = "2.2.2.240514"
description = "Type annotations for pandas" description = "Type annotations for pandas"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "pandas_stubs-2.2.1.240316-py3-none-any.whl", hash = "sha256:0126a26451a37cb893ea62357ca87ba3d181bd999ec8ba2ca5602e20207d6682"}, {file = "pandas_stubs-2.2.2.240514-py3-none-any.whl", hash = "sha256:5d6f64d45a98bc94152a0f76fa648e598cd2b9ba72302fd34602479f0c391a53"},
{file = "pandas_stubs-2.2.1.240316.tar.gz", hash = "sha256:236a4f812fb6b1922e9607ff09e427f6d8540c421c9e5a40e3e4ddf7adac7f05"}, {file = "pandas_stubs-2.2.2.240514.tar.gz", hash = "sha256:85b20da44a62c80eb8389bcf4cbfe31cce1cafa8cca4bf1fc75ec45892e72ce8"},
] ]
[package.dependencies] [package.dependencies]
@ -6635,6 +6635,7 @@ optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"}, {file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"},
{file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"},
] ]
[package.dependencies] [package.dependencies]
@ -9491,76 +9492,89 @@ devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)
[[package]] [[package]]
name = "ujson" name = "ujson"
version = "5.9.0" version = "5.10.0"
description = "Ultra fast JSON encoder and decoder for Python" description = "Ultra fast JSON encoder and decoder for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"},
{file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"},
{file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"},
{file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"},
{file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"},
{file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"},
{file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"},
{file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"},
{file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"},
{file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"},
{file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"},
{file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"},
{file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"},
{file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"},
{file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"},
{file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"},
{file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"},
{file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"},
{file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"},
{file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"},
{file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"},
{file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"},
{file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"},
{file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"},
{file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"},
{file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"},
{file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"},
{file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"},
{file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"},
{file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"},
{file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"},
{file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"},
{file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"},
{file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"},
{file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"},
{file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"},
{file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"},
{file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"},
{file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"},
{file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"},
{file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"},
{file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"},
{file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"},
{file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"},
{file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"},
{file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"},
{file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"},
{file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"},
{file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"},
{file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"},
{file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"},
{file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"},
{file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"},
{file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"},
{file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"},
{file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"},
{file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"},
{file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"},
{file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"},
{file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"},
{file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"},
{file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"},
{file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"},
{file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"},
{file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"},
{file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"},
{file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"},
{file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"},
{file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"},
{file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"},
{file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"},
{file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"},
{file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"},
{file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"},
{file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"},
{file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"},
{file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"},
{file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"},
] ]
[[package]] [[package]]
@ -9685,13 +9699,13 @@ files = [
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "20.26.1" version = "20.26.2"
description = "Virtual Python Environment builder" description = "Virtual Python Environment builder"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"},
{file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"},
] ]
[package.dependencies] [package.dependencies]
@ -9803,13 +9817,13 @@ files = [
[[package]] [[package]]
name = "weaviate-client" name = "weaviate-client"
version = "4.6.0" version = "4.6.1"
description = "A python native Weaviate client" description = "A python native Weaviate client"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "weaviate_client-4.6.0-py3-none-any.whl", hash = "sha256:4c9608ba5d495d1fe28e451aca057086cb55242bb8432bfb0008f433eef7a6ab"}, {file = "weaviate_client-4.6.1-py3-none-any.whl", hash = "sha256:0db94e5c87f92c9490d90f6de6637baa55dc0d92a3750dd782667b7e5d718ca3"},
{file = "weaviate_client-4.6.0.tar.gz", hash = "sha256:5cc6c8b2a6f9122b34ffa688070e5060ab044fe72a4a9233eb9938c6067866b8"}, {file = "weaviate_client-4.6.1.tar.gz", hash = "sha256:6d3351f20dd98e3936351473c305ff7660c5ec4bd8b5ad1f11593ef60e8058f6"},
] ]
[package.dependencies] [package.dependencies]

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "1.0.0a32" version = "1.0.0a33"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"] authors = ["Langflow <contact@langflow.org>"]
maintainers = [ maintainers = [

View file

@ -131,3 +131,4 @@ dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .pyre/
*.db

View file

@ -35,7 +35,7 @@ class SubFlowComponent(CustomComponent):
build_config["flow_name"]["options"] = self.get_flow_names() build_config["flow_name"]["options"] = self.get_flow_names()
# Clean up the build config # Clean up the build config
for key in list(build_config.keys()): for key in list(build_config.keys()):
if key not in self.field_order + ["code", "_type"]: if key not in self.field_order + ["code", "_type", "get_final_results_only"]:
del build_config[key] del build_config[key]
if field_value is not None and field_name == "flow_name": if field_value is not None and field_name == "flow_name":
try: try:
@ -85,20 +85,29 @@ class SubFlowComponent(CustomComponent):
"display_name": "Tweaks", "display_name": "Tweaks",
"info": "Tweaks to apply to the flow.", "info": "Tweaks to apply to the flow.",
}, },
"get_final_results_only": {
"display_name": "Get Final Results Only",
"info": "If False, the output will contain all outputs from the flow.",
"advanced": True,
},
} }
def build_records_from_result_data(self, result_data: ResultData) -> List[Record]: def build_records_from_result_data(self, result_data: ResultData, get_final_results_only: bool) -> List[Record]:
messages = result_data.messages messages = result_data.messages
if not messages: if not messages:
return [] return []
records = [] records = []
for message in messages: for message in messages:
message_dict = message if isinstance(message, dict) else message.model_dump() message_dict = message if isinstance(message, dict) else message.model_dump()
record = Record(data={"result": result_data.model_dump(), "message": message_dict.get("message", "")}) if get_final_results_only:
result_data_dict = result_data.model_dump()
results = result_data_dict.get("results", {})
inner_result = results.get("result", {})
record = Record(data={"result": inner_result, "message": message_dict}, text_key="result")
records.append(record) records.append(record)
return records return records
async def build(self, flow_name: str, **kwargs) -> List[Record]: async def build(self, flow_name: str, get_final_results_only: bool = True, **kwargs) -> List[Record]:
tweaks = {key: {"input_value": value} for key, value in kwargs.items()} tweaks = {key: {"input_value": value} for key, value in kwargs.items()}
run_outputs: List[Optional[RunOutputs]] = await self.run_flow( run_outputs: List[Optional[RunOutputs]] = await self.run_flow(
tweaks=tweaks, tweaks=tweaks,
@ -112,7 +121,7 @@ class SubFlowComponent(CustomComponent):
if run_output is not None: if run_output is not None:
for output in run_output.outputs: for output in run_output.outputs:
if output: if output:
records.extend(self.build_records_from_result_data(output)) records.extend(self.build_records_from_result_data(output, get_final_results_only))
self.status = records self.status = records
logger.debug(records) logger.debug(records)

View file

@ -7,13 +7,12 @@ from typing import Any, List, Optional, Tuple, Type
import orjson import orjson
import yaml import yaml
from langflow.services.settings.constants import VARIABLES_TO_GET_FROM_ENVIRONMENT
from loguru import logger from loguru import logger
from pydantic import field_validator, validator from pydantic import field_validator
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict from pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource, SettingsConfigDict
from langflow.services.settings.constants import VARIABLES_TO_GET_FROM_ENVIRONMENT
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components") # BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components") BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
@ -76,6 +75,10 @@ class Settings(BaseSettings):
# Define the default LANGFLOW_DIR # Define the default LANGFLOW_DIR
CONFIG_DIR: Optional[str] = None CONFIG_DIR: Optional[str] = None
# Define if langflow db should be saved in config dir or
# in the langflow directory
SAVE_DB_IN_CONFIG_DIR: bool = False
"""Define if langflow database should be saved in LANGFLOW_CONFIG_DIR or in the langflow directory (i.e. in the package directory)."""
DEV: bool = False DEV: bool = False
DATABASE_URL: Optional[str] = None DATABASE_URL: Optional[str] = None
@ -113,7 +116,7 @@ class Settings(BaseSettings):
variables_to_get_from_environment: list[str] = VARIABLES_TO_GET_FROM_ENVIRONMENT variables_to_get_from_environment: list[str] = VARIABLES_TO_GET_FROM_ENVIRONMENT
"""List of environment variables to get from the environment and store in the database.""" """List of environment variables to get from the environment and store in the database."""
@validator("CONFIG_DIR", pre=True, allow_reuse=True) @field_validator("CONFIG_DIR", mode="before")
def set_langflow_dir(cls, value): def set_langflow_dir(cls, value):
if not value: if not value:
from platformdirs import user_cache_dir from platformdirs import user_cache_dir
@ -136,8 +139,8 @@ class Settings(BaseSettings):
return str(value) return str(value)
@validator("DATABASE_URL", pre=True) @field_validator("DATABASE_URL", mode="before")
def set_database_url(cls, value, values): def set_database_url(cls, value, info):
if not value: if not value:
logger.debug("No database_url provided, trying LANGFLOW_DATABASE_URL env variable") logger.debug("No database_url provided, trying LANGFLOW_DATABASE_URL env variable")
if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"): if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"):
@ -148,29 +151,36 @@ class Settings(BaseSettings):
# Originally, we used sqlite:///./langflow.db # Originally, we used sqlite:///./langflow.db
# so we need to migrate to the new format # so we need to migrate to the new format
# if there is a database in that location # if there is a database in that location
if not values["CONFIG_DIR"]: if not info.data["CONFIG_DIR"]:
raise ValueError("CONFIG_DIR not set, please set it or provide a DATABASE_URL") raise ValueError("CONFIG_DIR not set, please set it or provide a DATABASE_URL")
from langflow.version import is_pre_release # type: ignore from langflow.version import is_pre_release # type: ignore
if info.data["SAVE_DB_IN_CONFIG_DIR"]:
database_dir = info.data["CONFIG_DIR"]
logger.debug(f"Saving database to CONFIG_DIR: {database_dir}")
else:
database_dir = Path(__file__).parent.parent.parent.resolve()
logger.debug(f"Saving database to langflow directory: {database_dir}")
pre_db_file_name = "langflow-pre.db" pre_db_file_name = "langflow-pre.db"
db_file_name = "langflow.db" db_file_name = "langflow.db"
new_pre_path = f"{values['CONFIG_DIR']}/{pre_db_file_name}" new_pre_path = f"{database_dir}/{pre_db_file_name}"
new_path = f"{values['CONFIG_DIR']}/{db_file_name}" new_path = f"{database_dir}/{db_file_name}"
final_path = None final_path = None
if is_pre_release: if is_pre_release:
if Path(new_pre_path).exists(): if Path(new_pre_path).exists():
final_path = new_pre_path final_path = new_pre_path
elif Path(new_path).exists(): elif Path(new_path).exists() and info.data["SAVE_DB_IN_CONFIG_DIR"]:
# We need to copy the current db to the new location # We need to copy the current db to the new location
logger.debug("Copying existing database to new location") logger.debug("Copying existing database to new location")
copy2(new_path, new_pre_path) copy2(new_path, new_pre_path)
logger.debug(f"Copied existing database to {new_pre_path}") logger.debug(f"Copied existing database to {new_pre_path}")
elif Path(f"./{db_file_name}").exists(): elif Path(f"./{db_file_name}").exists() and info.data["SAVE_DB_IN_CONFIG_DIR"]:
logger.debug("Copying existing database to new location") logger.debug("Copying existing database to new location")
copy2(f"./{db_file_name}", new_pre_path) copy2(f"./{db_file_name}", new_pre_path)
logger.debug(f"Copied existing database to {new_pre_path}") logger.debug(f"Copied existing database to {new_pre_path}")
else: else:
logger.debug(f"Database already exists at {new_pre_path}, using it") logger.debug(f"Creating new database at {new_pre_path}")
final_path = new_pre_path final_path = new_pre_path
else: else:
if Path(new_path).exists(): if Path(new_path).exists():
@ -311,3 +321,6 @@ def load_settings_from_yaml(file_path: str) -> Settings:
logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}") logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
return Settings(**settings_dict) return Settings(**settings_dict)
return Settings(**settings_dict)
return Settings(**settings_dict)
return Settings(**settings_dict)

View file

@ -1,11 +1,10 @@
import os import os
import yaml import yaml
from loguru import logger
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.settings.auth import AuthSettings from langflow.services.settings.auth import AuthSettings
from langflow.services.settings.base import Settings from langflow.services.settings.base import Settings
from loguru import logger
class SettingsService(Service): class SettingsService(Service):
@ -31,7 +30,7 @@ class SettingsService(Service):
for key in settings_dict: for key in settings_dict:
if key not in Settings.model_fields.keys(): if key not in Settings.model_fields.keys():
raise KeyError(f"Key {key} not found in settings") logger.warning(f"Key {key} not found in settings")
logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}") logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
settings = Settings(**settings_dict) settings = Settings(**settings_dict)

View file

@ -33,10 +33,13 @@ class VariableService(Service):
select(Variable).where(Variable.user_id == user_id, Variable.name == var) select(Variable).where(Variable.user_id == user_id, Variable.name == var)
).first(): ).first():
try: try:
value = os.environ[var]
if isinstance(value, str):
value = value.strip()
self.create_variable( self.create_variable(
user_id=user_id, user_id=user_id,
name=var, name=var,
value=os.environ[var], value=value,
default_fields=[], default_fields=[],
_type="Credential", _type="Credential",
session=session, session=session,

View file

@ -1357,13 +1357,13 @@ source = ["Cython (>=3.0.10)"]
[[package]] [[package]]
name = "mako" name = "mako"
version = "1.3.3" version = "1.3.5"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages." description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40"}, {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"},
{file = "Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73"}, {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"},
] ]
[package.dependencies] [package.dependencies]

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow-base" name = "langflow-base"
version = "0.0.43" version = "0.0.44"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Langflow <contact@langflow.org>"] authors = ["Langflow <contact@langflow.org>"]
maintainers = [ maintainers = [

File diff suppressed because it is too large Load diff

View file

@ -18,12 +18,12 @@ export default function CodeAreaComponent({
setOpen, setOpen,
}: CodeAreaComponentType) { }: CodeAreaComponentType) {
const [myValue, setMyValue] = useState( const [myValue, setMyValue] = useState(
typeof value == "string" ? value : JSON.stringify(value) typeof value == "string" ? value : JSON.stringify(value),
); );
useEffect(() => { useEffect(() => {
if (disabled && myValue !== "") { if (disabled && myValue !== "") {
setMyValue(""); setMyValue("");
onChange(""); onChange("", true);
} }
}, [disabled]); }, [disabled]);

View file

@ -43,6 +43,7 @@ import KeypairListComponent from "../keypairListComponent";
import ShadTooltip from "../shadTooltipComponent"; import ShadTooltip from "../shadTooltipComponent";
import { Label } from "../ui/label"; import { Label } from "../ui/label";
import { Switch } from "../ui/switch"; import { Switch } from "../ui/switch";
import ExportModal from "../../modals/exportModal";
export default function CodeTabsComponent({ export default function CodeTabsComponent({
flow, flow,
@ -167,6 +168,12 @@ export default function CodeTabsComponent({
Tweaks Tweaks
</Label> </Label>
</div> </div>
<ExportModal>
<div className="flex cursor-pointer items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300">
<IconComponent name="FileDown" className="h-4 w-4" />
Export Flow
</div>
</ExportModal>
{Number(activeTab) < 4 && ( {Number(activeTab) < 4 && (
<> <>

View file

@ -16,7 +16,7 @@ export default function FloatComponent({
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange(""); onChange("", true);
} }
}, [disabled]); }, [disabled]);

View file

@ -48,7 +48,7 @@ export default function InputComponent({
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
if (disabled && value && onChange && value !== "") { if (disabled && value && onChange && value !== "") {
onChange(""); onChange("", true);
} }
}, [disabled]); }, [disabled]);

View file

@ -27,7 +27,7 @@ export default function InputFileComponent({
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
setMyValue(""); setMyValue("");
onChange(""); onChange("", true);
onFileChange(""); onFileChange("");
} }
}, [disabled, onChange]); }, [disabled, onChange]);
@ -106,8 +106,8 @@ export default function InputFileComponent({
editNode editNode
? "input-edit-node input-dialog text-muted-foreground" ? "input-edit-node input-dialog text-muted-foreground"
: disabled : disabled
? "input-disable input-dialog primary-input" ? "input-disable input-dialog primary-input"
: "input-dialog primary-input text-muted-foreground" : "input-dialog primary-input text-muted-foreground"
} }
> >
{myValue !== "" ? myValue : "No file"} {myValue !== "" ? myValue : "No file"}

View file

@ -19,15 +19,15 @@ export default function InputGlobalComponent({
editNode = false, editNode = false,
}: InputGlobalComponentType): JSX.Element { }: InputGlobalComponentType): JSX.Element {
const globalVariablesEntries = useGlobalVariablesStore( const globalVariablesEntries = useGlobalVariablesStore(
(state) => state.globalVariablesEntries (state) => state.globalVariablesEntries,
); );
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId); const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
const unavaliableFields = useGlobalVariablesStore( const unavaliableFields = useGlobalVariablesStore(
(state) => state.unavaliableFields (state) => state.unavaliableFields,
); );
const removeGlobalVariable = useGlobalVariablesStore( const removeGlobalVariable = useGlobalVariablesStore(
(state) => state.removeGlobalVariable (state) => state.removeGlobalVariable,
); );
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
@ -38,7 +38,7 @@ export default function InputGlobalComponent({
data.node?.template[name].load_from_db data.node?.template[name].load_from_db
) { ) {
setTimeout(() => { setTimeout(() => {
onChange(""); onChange("", true);
setDb(false); setDb(false);
}, 100); }, 100);
} }
@ -129,7 +129,7 @@ export default function InputGlobalComponent({
<ForwardedIconComponent <ForwardedIconComponent
name="Trash2" name="Trash2"
className={cn( className={cn(
"h-4 w-4 text-primary opacity-0 hover:text-status-red group-hover:opacity-100" "h-4 w-4 text-primary opacity-0 hover:text-status-red group-hover:opacity-100",
)} )}
aria-hidden="true" aria-hidden="true"
/> />
@ -146,8 +146,8 @@ export default function InputGlobalComponent({
onChange(value); onChange(value);
setDb(value !== "" ? true : false); setDb(value !== "" ? true : false);
}} }}
onChange={(value) => { onChange={(value, skipSnapshot) => {
onChange(value); onChange(value, skipSnapshot);
setDb(false); setDb(false);
}} }}
/> />

View file

@ -19,7 +19,7 @@ export default function IntComponent({
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange(""); onChange("", true);
} }
}, [disabled, onChange]); }, [disabled, onChange]);

View file

@ -18,7 +18,7 @@ export default function PromptAreaComponent({
}: PromptAreaComponentType): JSX.Element { }: PromptAreaComponentType): JSX.Element {
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange(""); onChange("", true);
} }
}, [disabled]); }, [disabled]);

View file

@ -16,7 +16,7 @@ export default function TextAreaComponent({
// Clear text area // Clear text area
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange(""); onChange("", true);
} }
}, [disabled]); }, [disabled]);

View file

@ -159,11 +159,12 @@ export default function ParameterComponent({
const handleOnNewValue = async ( const handleOnNewValue = async (
newValue: string | string[] | boolean | Object[], newValue: string | string[] | boolean | Object[],
skipSnapshot: boolean | undefined = false,
): Promise<void> => { ): Promise<void> => {
const nodeTemplate = data.node!.template[name]; const nodeTemplate = data.node!.template[name];
const currentValue = nodeTemplate.value; const currentValue = nodeTemplate.value;
if (currentValue !== newValue) { if (currentValue !== newValue && !skipSnapshot) {
takeSnapshot(); takeSnapshot();
} }

View file

@ -56,7 +56,11 @@ export const GradientGroup = (props) => {
</linearGradient> </linearGradient>
</defs> </defs>
</svg> </svg>
<ForwardedIconComponent name="Combine" stroke="url(#grad3)" {...props} /> <ForwardedIconComponent
name="Combine"
stroke={`${props.disabled ? "#64748B" : "url(#grad3)"}`}
{...props}
/>
</> </>
); );
}; };

View file

@ -51,19 +51,19 @@ export default function Page({
}): JSX.Element { }): JSX.Element {
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const autoSaveCurrentFlow = useFlowsManagerStore( const autoSaveCurrentFlow = useFlowsManagerStore(
(state) => state.autoSaveCurrentFlow (state) => state.autoSaveCurrentFlow,
); );
const types = useTypesStore((state) => state.types); const types = useTypesStore((state) => state.types);
const templates = useTypesStore((state) => state.templates); const templates = useTypesStore((state) => state.templates);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [showCanvas, setSHowCanvas] = useState( const [showCanvas, setSHowCanvas] = useState(
Object.keys(templates).length > 0 && Object.keys(types).length > 0 Object.keys(templates).length > 0 && Object.keys(types).length > 0,
); );
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
const setReactFlowInstance = useFlowStore( const setReactFlowInstance = useFlowStore(
(state) => state.setReactFlowInstance (state) => state.setReactFlowInstance,
); );
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges); const edges = useFlowStore((state) => state.edges);
@ -80,10 +80,10 @@ export default function Page({
const paste = useFlowStore((state) => state.paste); const paste = useFlowStore((state) => state.paste);
const resetFlow = useFlowStore((state) => state.resetFlow); const resetFlow = useFlowStore((state) => state.resetFlow);
const lastCopiedSelection = useFlowStore( const lastCopiedSelection = useFlowStore(
(state) => state.lastCopiedSelection (state) => state.lastCopiedSelection,
); );
const setLastCopiedSelection = useFlowStore( const setLastCopiedSelection = useFlowStore(
(state) => state.setLastCopiedSelection (state) => state.setLastCopiedSelection,
); );
const onConnect = useFlowStore((state) => state.onConnect); const onConnect = useFlowStore((state) => state.onConnect);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
@ -106,7 +106,7 @@ export default function Page({
clonedSelection!, clonedSelection!,
clonedNodes, clonedNodes,
clonedEdges, clonedEdges,
getRandomName() getRandomName(),
); );
const newGroupNode = generateNodeFromFlow(newFlow, getNodeId); const newGroupNode = generateNodeFromFlow(newFlow, getNodeId);
const newEdges = reconnectEdges(newGroupNode, removedEdges); const newEdges = reconnectEdges(newGroupNode, removedEdges);
@ -114,8 +114,8 @@ export default function Page({
...clonedNodes.filter( ...clonedNodes.filter(
(oldNodes) => (oldNodes) =>
!clonedSelection?.nodes.some( !clonedSelection?.nodes.some(
(selectionNode) => selectionNode.id === oldNodes.id (selectionNode) => selectionNode.id === oldNodes.id,
) ),
), ),
newGroupNode, newGroupNode,
]); ]);
@ -125,8 +125,8 @@ export default function Page({
!clonedSelection!.nodes.some( !clonedSelection!.nodes.some(
(selectionNode) => (selectionNode) =>
selectionNode.id === oldEdge.target || selectionNode.id === oldEdge.target ||
selectionNode.id === oldEdge.source selectionNode.id === oldEdge.source,
) ),
), ),
...newEdges, ...newEdges,
]); ]);
@ -141,7 +141,8 @@ export default function Page({
const setNode = useFlowStore((state) => state.setNode); const setNode = useFlowStore((state) => state.setNode);
useEffect(() => { useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
const selectedNode = nodes.filter((obj) => obj.selected); const selectedNode = lastSelection?.nodes ?? [];
const selectedEdges = lastSelection?.edges ?? [];
if ( if (
selectionMenuVisible && selectionMenuVisible &&
(event.ctrlKey || event.metaKey) && (event.ctrlKey || event.metaKey) &&
@ -174,11 +175,11 @@ export default function Page({
) { ) {
event.preventDefault(); event.preventDefault();
paste( paste(
{ nodes: selectedNode, edges: [] }, { nodes: selectedNode, edges: selectedEdges },
{ {
x: position.current.x, x: position.current.x,
y: position.current.y, y: position.current.y,
} },
); );
} }
if (!isWrappedWithClass(event, "noundo")) { if (!isWrappedWithClass(event, "noundo")) {
@ -274,7 +275,7 @@ export default function Page({
useEffect(() => { useEffect(() => {
setSHowCanvas( setSHowCanvas(
Object.keys(templates).length > 0 && Object.keys(types).length > 0 Object.keys(templates).length > 0 && Object.keys(types).length > 0,
); );
}, [templates, types]); }, [templates, types]);
@ -283,7 +284,7 @@ export default function Page({
takeSnapshot(); takeSnapshot();
onConnect(params); onConnect(params);
}, },
[takeSnapshot, onConnect] [takeSnapshot, onConnect],
); );
const onNodeDragStart: NodeDragHandler = useCallback(() => { const onNodeDragStart: NodeDragHandler = useCallback(() => {
@ -324,7 +325,7 @@ export default function Page({
// Extract the data from the drag event and parse it as a JSON object // Extract the data from the drag event and parse it as a JSON object
const data: { type: string; node?: APIClassType } = JSON.parse( const data: { type: string; node?: APIClassType } = JSON.parse(
event.dataTransfer.getData("nodedata") event.dataTransfer.getData("nodedata"),
); );
const newId = getNodeId(data.type); const newId = getNodeId(data.type);
@ -340,7 +341,7 @@ export default function Page({
}; };
paste( paste(
{ nodes: [newNode], edges: [] }, { nodes: [newNode], edges: [] },
{ x: event.clientX, y: event.clientY } { x: event.clientX, y: event.clientY },
); );
} else if (event.dataTransfer.types.some((types) => types === "Files")) { } else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot(); takeSnapshot();
@ -369,7 +370,7 @@ export default function Page({
} }
}, },
// Specify dependencies for useCallback // Specify dependencies for useCallback
[getNodeId, setNodes, takeSnapshot, paste] [getNodeId, setNodes, takeSnapshot, paste],
); );
const onEdgeUpdateStart = useCallback(() => { const onEdgeUpdateStart = useCallback(() => {
@ -385,7 +386,7 @@ export default function Page({
setEdges((els) => updateEdge(oldEdge, newConnection, els)); setEdges((els) => updateEdge(oldEdge, newConnection, els));
} }
}, },
[setEdges] [setEdges],
); );
const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => { const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => {
@ -418,7 +419,7 @@ export default function Page({
(flow: OnSelectionChangeParams): void => { (flow: OnSelectionChangeParams): void => {
setLastSelection(flow); setLastSelection(flow);
}, },
[] [],
); );
const onPaneClick = useCallback((flow) => { const onPaneClick = useCallback((flow) => {
@ -482,6 +483,7 @@ export default function Page({
></Controls> ></Controls>
)} )}
<SelectionMenu <SelectionMenu
lastSelection={lastSelection}
isVisible={selectionMenuVisible} isVisible={selectionMenuVisible}
nodes={lastSelection?.nodes} nodes={lastSelection?.nodes}
onClick={() => { onClick={() => {

View file

@ -1,11 +1,31 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { NodeToolbar } from "reactflow"; import { NodeToolbar } from "reactflow";
import { GradientGroup } from "../../../../icons/GradientSparkles"; import { GradientGroup } from "../../../../icons/GradientSparkles";
export default function SelectionMenu({ onClick, nodes, isVisible }) { import { validateSelection } from "../../../../utils/reactflowUtils";
import useFlowStore from "../../../../stores/flowStore";
export default function SelectionMenu({
onClick,
nodes,
isVisible,
lastSelection,
}) {
const edges = useFlowStore((state) => state.edges);
const [disable, setDisable] = useState<boolean>(
lastSelection && edges.length > 0
? validateSelection(lastSelection!, edges).length > 0
: false,
);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [isTransitioning, setIsTransitioning] = useState(false); const [isTransitioning, setIsTransitioning] = useState(false);
const [lastNodes, setLastNodes] = useState(nodes); const [lastNodes, setLastNodes] = useState(nodes);
useEffect(() => {
if (isOpen) {
return setDisable(validateSelection(lastSelection!, edges).length > 0);
}
setDisable(false);
}, [isOpen, setIsOpen]);
// nodes get saved to not be gone after the toolbar closes // nodes get saved to not be gone after the toolbar closes
useEffect(() => { useEffect(() => {
setLastNodes(nodes); setLastNodes(nodes);
@ -42,13 +62,15 @@ export default function SelectionMenu({ onClick, nodes, isVisible }) {
} }
> >
<button <button
className="flex h-full w-full items-center justify-between text-sm hover:text-indigo-500" className={`${disable ? "flex h-full w-full cursor-not-allowed items-center justify-between text-sm text-muted-foreground" : "flex h-full w-full items-center justify-between text-sm hover:text-indigo-500"}`}
onClick={onClick} onClick={onClick}
disabled={disable}
> >
<GradientGroup <GradientGroup
strokeWidth={1.5} strokeWidth={1.5}
size={22} size={22}
className="text-primary" className="text-primary"
disabled={disable}
/> />
Group Group
</button> </button>

View file

@ -49,7 +49,7 @@ import FlowPage from "../pages/FlowPage";
// this is our useStore hook that we can use in our components to get parts of the store and call actions // this is our useStore hook that we can use in our components to get parts of the store and call actions
const useFlowStore = create<FlowStoreType>((set, get) => ({ const useFlowStore = create<FlowStoreType>((set, get) => ({
onFlowPage: false, onFlowPage: false,
setOnFlowPage:(FlowPage=>set({onFlowPage:FlowPage})), setOnFlowPage: (FlowPage) => set({ onFlowPage: FlowPage }),
flowState: undefined, flowState: undefined,
flowBuildStatus: {}, flowBuildStatus: {},
nodes: [], nodes: [],
@ -80,7 +80,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
updateFlowPool: ( updateFlowPool: (
nodeId: string, nodeId: string,
data: FlowPoolObjectType | ChatOutputType | chatInputType, data: FlowPoolObjectType | ChatOutputType | chatInputType,
buildId?: string buildId?: string,
) => { ) => {
let newFlowPool = cloneDeep({ ...get().flowPool }); let newFlowPool = cloneDeep({ ...get().flowPool });
if (!newFlowPool[nodeId]) { if (!newFlowPool[nodeId]) {
@ -152,7 +152,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
edges: applyEdgeChanges(changes, get().edges), edges: applyEdgeChanges(changes, get().edges),
}); });
}, },
setNodes: (change,skipSave=false) => { setNodes: (change, skipSave = false) => {
let newChange = typeof change === "function" ? change(get().nodes) : change; let newChange = typeof change === "function" ? change(get().nodes) : change;
let newEdges = cleanEdges(newChange, get().edges); let newEdges = cleanEdges(newChange, get().edges);
const { inputs, outputs } = getInputsAndOutputs(newChange); const { inputs, outputs } = getInputsAndOutputs(newChange);
@ -171,11 +171,11 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
flowsManager.autoSaveCurrentFlow( flowsManager.autoSaveCurrentFlow(
newChange, newChange,
newEdges, newEdges,
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 },
); );
} }
}, },
setEdges: (change,skipSave=false) => { setEdges: (change, skipSave = false) => {
let newChange = typeof change === "function" ? change(get().edges) : change; let newChange = typeof change === "function" ? change(get().edges) : change;
set({ set({
edges: newChange, edges: newChange,
@ -187,7 +187,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
flowsManager.autoSaveCurrentFlow( flowsManager.autoSaveCurrentFlow(
get().nodes, get().nodes,
newChange, newChange,
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 },
); );
} }
}, },
@ -205,7 +205,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
return newChange; return newChange;
} }
return node; return node;
}) }),
); );
}, },
getNode: (id: string) => { getNode: (id: string) => {
@ -216,8 +216,8 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
get().nodes.filter((node) => get().nodes.filter((node) =>
typeof nodeId === "string" typeof nodeId === "string"
? node.id !== nodeId ? node.id !== nodeId
: !nodeId.includes(node.id) : !nodeId.includes(node.id),
) ),
); );
}, },
deleteEdge: (edgeId) => { deleteEdge: (edgeId) => {
@ -225,13 +225,11 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
get().edges.filter((edge) => get().edges.filter((edge) =>
typeof edgeId === "string" typeof edgeId === "string"
? edge.id !== edgeId ? edge.id !== edgeId
: !edgeId.includes(edge.id) : !edgeId.includes(edge.id),
) ),
); );
}, },
paste: (selection, position) => { paste: (selection, position) => {
function updateGroup() {}
if ( if (
selection.nodes.some((node) => node.data.type === "ChatInput") && selection.nodes.some((node) => node.data.type === "ChatInput") &&
checkChatInput(get().nodes) checkChatInput(get().nodes)
@ -268,8 +266,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
let newId = getNodeId(node.data.type); let newId = getNodeId(node.data.type);
idsMap[node.id] = newId; idsMap[node.id] = newId;
updateGroupRecursion(node, selection.edges);
// Create a new node object // Create a new node object
const newNode: NodeType = { const newNode: NodeType = {
id: newId, id: newId,
@ -283,6 +279,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
id: newId, id: newId,
}, },
}; };
updateGroupRecursion(newNode, selection.edges);
// Add the new node to the list of nodes in state // Add the new node to the list of nodes in state
newNodes = newNodes newNodes = newNodes
@ -295,7 +292,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
let source = idsMap[edge.source]; let source = idsMap[edge.source];
let target = idsMap[edge.target]; let target = idsMap[edge.target];
const sourceHandleObject: sourceHandleType = scapeJSONParse( const sourceHandleObject: sourceHandleType = scapeJSONParse(
edge.sourceHandle! edge.sourceHandle!,
); );
let sourceHandle = scapedJSONStringfy({ let sourceHandle = scapedJSONStringfy({
...sourceHandleObject, ...sourceHandleObject,
@ -305,7 +302,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
edge.data.sourceHandle = sourceHandleObject; edge.data.sourceHandle = sourceHandleObject;
const targetHandleObject: targetHandleType = scapeJSONParse( const targetHandleObject: targetHandleType = scapeJSONParse(
edge.targetHandle! edge.targetHandle!,
); );
let targetHandle = scapedJSONStringfy({ let targetHandle = scapedJSONStringfy({
...targetHandleObject, ...targetHandleObject,
@ -326,7 +323,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
className: "stroke-gray-900 ", className: "stroke-gray-900 ",
selected: false, selected: false,
}, },
newEdges.map((edge) => ({ ...edge, selected: false })) newEdges.map((edge) => ({ ...edge, selected: false })),
); );
}); });
get().setEdges(newEdges); get().setEdges(newEdges);
@ -345,10 +342,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
}); });
const newNodes = get().nodes.filter( const newNodes = get().nodes.filter(
(node) => !nodesIdsSelected.includes(node.id) (node) => !nodesIdsSelected.includes(node.id),
); );
const newEdges = get().edges.filter( const newEdges = get().edges.filter(
(edge) => !edgesIdsSelected.includes(edge.id) (edge) => !edgesIdsSelected.includes(edge.id),
); );
set({ nodes: newNodes, edges: newEdges }); set({ nodes: newNodes, edges: newEdges });
@ -406,7 +403,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
style: { stroke: "#555" }, style: { stroke: "#555" },
className: "stroke-foreground stroke-connection", className: "stroke-foreground stroke-connection",
}, },
oldEdges oldEdges,
); );
return newEdges; return newEdges;
@ -416,7 +413,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
.autoSaveCurrentFlow( .autoSaveCurrentFlow(
get().nodes, get().nodes,
newEdges, newEdges,
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 },
); );
}, },
unselectAll: () => { unselectAll: () => {
@ -447,7 +444,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
function validateSubgraph(nodes: string[]) { function validateSubgraph(nodes: string[]) {
const errorsObjs = validateNodes( const errorsObjs = validateNodes(
get().nodes.filter((node) => nodes.includes(node.id)), get().nodes.filter((node) => nodes.includes(node.id)),
get().edges get().edges,
); );
const errors = errorsObjs.map((obj) => obj.errors).flat(); const errors = errorsObjs.map((obj) => obj.errors).flat();
@ -466,7 +463,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
function handleBuildUpdate( function handleBuildUpdate(
vertexBuildData: VertexBuildTypeAPI, vertexBuildData: VertexBuildTypeAPI,
status: BuildStatus, status: BuildStatus,
runId: string runId: string,
) { ) {
if (vertexBuildData && vertexBuildData.inactivated_vertices) { if (vertexBuildData && vertexBuildData.inactivated_vertices) {
get().removeFromVerticesBuild(vertexBuildData.inactivated_vertices); get().removeFromVerticesBuild(vertexBuildData.inactivated_vertices);
@ -484,11 +481,11 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
// next_vertices_ids should be next_vertices_ids without the inactivated vertices // next_vertices_ids should be next_vertices_ids without the inactivated vertices
const next_vertices_ids = vertexBuildData.next_vertices_ids.filter( const next_vertices_ids = vertexBuildData.next_vertices_ids.filter(
(id) => !vertexBuildData.inactivated_vertices?.includes(id) (id) => !vertexBuildData.inactivated_vertices?.includes(id),
); );
const nextVertices: VertexLayerElementType[] = zip( const nextVertices: VertexLayerElementType[] = zip(
next_vertices_ids, next_vertices_ids,
vertexBuildData.top_level_vertices vertexBuildData.top_level_vertices,
).map(([id, reference]) => ({ id: id!, reference })); ).map(([id, reference]) => ({ id: id!, reference }));
const newLayers = [ const newLayers = [
@ -507,13 +504,13 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
}); });
get().updateBuildStatus( get().updateBuildStatus(
vertexBuildData.top_level_vertices, vertexBuildData.top_level_vertices,
BuildStatus.TO_BUILD BuildStatus.TO_BUILD,
); );
} }
get().addDataToFlowPool( get().addDataToFlowPool(
{ ...vertexBuildData, buildId: runId }, { ...vertexBuildData, buildId: runId },
vertexBuildData.id vertexBuildData.id,
); );
useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status); useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status);
@ -522,7 +519,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
const newFlowBuildStatus = { ...get().flowBuildStatus }; const newFlowBuildStatus = { ...get().flowBuildStatus };
// filter out the vertices that are not status // filter out the vertices that are not status
const verticesToUpdate = verticesIds?.filter( const verticesToUpdate = verticesIds?.filter(
(id) => newFlowBuildStatus[id]?.status !== BuildStatus.BUILT (id) => newFlowBuildStatus[id]?.status !== BuildStatus.BUILT,
); );
if (verticesToUpdate) { if (verticesToUpdate) {
@ -587,7 +584,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
verticesLayers: VertexLayerElementType[][]; verticesLayers: VertexLayerElementType[][];
runId: string; runId: string;
verticesToRun: string[]; verticesToRun: string[];
} | null } | null,
) => { ) => {
set({ verticesBuild: vertices }); set({ verticesBuild: vertices });
}, },
@ -612,7 +609,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
// that are going to be built // that are going to be built
verticesIds: get().verticesBuild!.verticesIds.filter( verticesIds: get().verticesBuild!.verticesIds.filter(
// keep the vertices that are not in the list of vertices to remove // keep the vertices that are not in the list of vertices to remove
(vertex) => !vertices.includes(vertex) (vertex) => !vertices.includes(vertex),
), ),
}, },
}); });

View file

@ -10,7 +10,7 @@ export type InputComponentType = {
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void; onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
value?: string; value?: string;
disabled?: boolean; disabled?: boolean;
onChange?: (value: string) => void; onChange?: (value: string, snapshot?: boolean) => void;
password: boolean; password: boolean;
required?: boolean; required?: boolean;
isForm?: boolean; isForm?: boolean;
@ -100,7 +100,7 @@ export type TextAreaComponentType = {
nodeClass?: APIClassType; nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType) => void; setNodeClass?: (value: APIClassType) => void;
disabled: boolean; disabled: boolean;
onChange: (value: string[] | string) => void; onChange: (value: string[] | string, skipSnapshot?: boolean) => void;
value: string; value: string;
editNode?: boolean; editNode?: boolean;
id?: string; id?: string;
@ -112,7 +112,7 @@ export type PromptAreaComponentType = {
nodeClass?: APIClassType; nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType, code?: string) => void; setNodeClass?: (value: APIClassType, code?: string) => void;
disabled: boolean; disabled: boolean;
onChange: (value: string[] | string) => void; onChange: (value: string[] | string, skipSnapshot?: boolean) => void;
value: string; value: string;
readonly?: boolean; readonly?: boolean;
editNode?: boolean; editNode?: boolean;
@ -122,7 +122,7 @@ export type PromptAreaComponentType = {
export type CodeAreaComponentType = { export type CodeAreaComponentType = {
setOpenModal?: (bool: boolean) => void; setOpenModal?: (bool: boolean) => void;
disabled: boolean; disabled: boolean;
onChange: (value: string[] | string) => void; onChange: (value: string[] | string, skipSnapshot?: boolean) => void;
value: string; value: string;
editNode?: boolean; editNode?: boolean;
nodeClass?: APIClassType; nodeClass?: APIClassType;
@ -137,7 +137,7 @@ export type CodeAreaComponentType = {
export type FileComponentType = { export type FileComponentType = {
IOInputProps?; IOInputProps?;
disabled: boolean; disabled: boolean;
onChange: (value: string[] | string) => void; onChange: (value: string[] | string, skipSnapshot?: boolean) => void;
value: string; value: string;
fileTypes: Array<string>; fileTypes: Array<string>;
onFileChange: (value: string) => void; onFileChange: (value: string) => void;
@ -170,7 +170,7 @@ export type IntComponentType = {
value: string; value: string;
disabled?: boolean; disabled?: boolean;
rangeSpec: RangeSpecType; rangeSpec: RangeSpecType;
onChange: (value: string) => void; onChange: (value: string, skipSnapshot?: boolean) => void;
editNode?: boolean; editNode?: boolean;
id?: string; id?: string;
}; };
@ -178,7 +178,7 @@ export type IntComponentType = {
export type FloatComponentType = { export type FloatComponentType = {
value: string; value: string;
disabled?: boolean; disabled?: boolean;
onChange: (value: string) => void; onChange: (value: string, skipSnapshot?: boolean) => void;
rangeSpec: RangeSpecType; rangeSpec: RangeSpecType;
editNode?: boolean; editNode?: boolean;
id?: string; id?: string;

View file

@ -994,7 +994,6 @@ export function updateEdgesIds(
if (targetHandle.proxy && idsMap[targetHandle.proxy!.id]) { if (targetHandle.proxy && idsMap[targetHandle.proxy!.id]) {
targetHandle.proxy!.id = idsMap[targetHandle.proxy!.id]; targetHandle.proxy!.id = idsMap[targetHandle.proxy!.id];
} }
console.log("edge", edge);
edge.data.targetHandle = targetHandle; edge.data.targetHandle = targetHandle;
edge.targetHandle = scapedJSONStringfy(targetHandle); edge.targetHandle = scapedJSONStringfy(targetHandle);
}); });
@ -1075,32 +1074,35 @@ export function expandGroupNode(
}); });
//update template values //update template values
Object.keys(template).forEach((key) => { Object.keys(template).forEach((key) => {
let { field, id } = template[key].proxy!; if (template[key].proxy) {
let nodeIndex = gNodes.findIndex((n) => n.id === id); let { field, id } = template[key].proxy!;
if (nodeIndex !== -1) { let nodeIndex = gNodes.findIndex((n) => n.id === id);
let proxy: { id: string; field: string } | undefined; if (nodeIndex !== -1) {
let display_name: string | undefined; let proxy: { id: string; field: string } | undefined;
let show = gNodes[nodeIndex].data.node!.template[field].show; let display_name: string | undefined;
let advanced = gNodes[nodeIndex].data.node!.template[field].advanced; let show = gNodes[nodeIndex].data.node!.template[field].show;
if (gNodes[nodeIndex].data.node!.template[field].display_name) { let advanced = gNodes[nodeIndex].data.node!.template[field].advanced;
display_name = if (gNodes[nodeIndex].data.node!.template[field].display_name) {
gNodes[nodeIndex].data.node!.template[field].display_name; display_name =
} else { gNodes[nodeIndex].data.node!.template[field].display_name;
display_name = gNodes[nodeIndex].data.node!.template[field].name; } else {
} display_name = gNodes[nodeIndex].data.node!.template[field].name;
if (gNodes[nodeIndex].data.node!.template[field].proxy) { }
proxy = gNodes[nodeIndex].data.node!.template[field].proxy; if (gNodes[nodeIndex].data.node!.template[field].proxy) {
} proxy = gNodes[nodeIndex].data.node!.template[field].proxy;
gNodes[nodeIndex].data.node!.template[field] = template[key]; }
gNodes[nodeIndex].data.node!.template[field].show = show; gNodes[nodeIndex].data.node!.template[field] = template[key];
gNodes[nodeIndex].data.node!.template[field].advanced = advanced; gNodes[nodeIndex].data.node!.template[field].show = show;
gNodes[nodeIndex].data.node!.template[field].display_name = display_name; gNodes[nodeIndex].data.node!.template[field].advanced = advanced;
// keep the nodes selected after ungrouping gNodes[nodeIndex].data.node!.template[field].display_name =
// gNodes[nodeIndex].selected = false; display_name;
if (proxy) { // keep the nodes selected after ungrouping
gNodes[nodeIndex].data.node!.template[field].proxy = proxy; // gNodes[nodeIndex].selected = false;
} else { if (proxy) {
delete gNodes[nodeIndex].data.node!.template[field].proxy; gNodes[nodeIndex].data.node!.template[field].proxy = proxy;
} else {
delete gNodes[nodeIndex].data.node!.template[field].proxy;
}
} }
} }
}); });

View file

@ -3,8 +3,7 @@ from typing import Dict, List, Optional
import pytest import pytest
from langflow.interface.utils import build_template_from_class from langflow.interface.utils import build_template_from_class
from langflow.utils.constants import CHAT_OPENAI_MODELS, OPENAI_MODELS from langflow.utils.util import build_template_from_function, get_base_classes, get_default_factory
from langflow.utils.util import build_template_from_function, format_dict, get_base_classes, get_default_factory
from pydantic import BaseModel from pydantic import BaseModel
@ -88,171 +87,6 @@ def test_build_template_from_class():
build_template_from_class("InvalidClass", type_to_cls_dict) build_template_from_class("InvalidClass", type_to_cls_dict)
# Test format_dict
def test_format_dict():
# Test 1: Optional type removal
input_dict = {
"field1": {"type": "Optional[str]", "required": False},
}
expected_output = {
"field1": {
"type": "str",
"required": False,
"list": False,
"show": False,
"password": False,
"multiline": False,
},
}
assert format_dict(input_dict) == expected_output
# Test 2: List type processing
input_dict = {
"field1": {"type": "List[str]", "required": False},
}
expected_output = {
"field1": {
"type": "str",
"required": False,
"list": True,
"show": False,
"password": False,
"multiline": False,
},
}
assert format_dict(input_dict) == expected_output
# Test 3: Mapping type replacement
input_dict = {
"field1": {"type": "Mapping[str, int]", "required": False},
}
expected_output = {
"field1": {
"type": "dict[str, int]", # Mapping type is replaced with dict which is replaced with code
"required": False,
"list": False,
"show": False,
"password": False,
"multiline": False,
},
}
assert format_dict(input_dict) == expected_output
# Test 4: Replace default value with actual value
input_dict = {
"field1": {"type": "str", "required": False, "default": "test"},
}
expected_output = {
"field1": {
"type": "str",
"required": False,
"list": False,
"show": False,
"password": False,
"multiline": False,
"value": "test",
},
}
assert format_dict(input_dict) == expected_output
# Test 5: Add password field
input_dict = {
"field1": {"type": "str", "required": False},
"api_key": {"type": "str", "required": False},
}
expected_output = {
"field1": {
"type": "str",
"required": False,
"list": False,
"show": False,
"password": False,
"multiline": False,
},
"api_key": {
"type": "str",
"required": False,
"list": False,
"show": True,
"password": True,
"multiline": False,
},
}
assert format_dict(input_dict) == expected_output
# Test 6: Add multiline
input_dict = {
"field1": {"type": "str", "required": False},
"prefix": {"type": "str", "required": False},
}
expected_output = {
"field1": {
"type": "str",
"required": False,
"list": False,
"show": False,
"password": False,
"multiline": False,
},
"prefix": {
"type": "str",
"required": False,
"list": False,
"show": True,
"password": False,
"multiline": True,
},
}
assert format_dict(input_dict) == expected_output
# Test 7: Check class name-specific cases (OpenAI, ChatOpenAI)
input_dict = {
"model_name": {"type": "str", "required": False},
}
expected_output_openai = {
"model_name": {
"type": "str",
"required": False,
"list": True,
"show": True,
"password": False,
"multiline": False,
"options": OPENAI_MODELS,
"value": "text-davinci-003",
},
}
expected_output_openai_chat = {
"model_name": {
"type": "str",
"required": False,
"list": True,
"show": True,
"password": False,
"multiline": False,
"options": CHAT_OPENAI_MODELS,
"value": "gpt-4-turbo-preview",
},
}
assert format_dict(input_dict, "OpenAI") == expected_output_openai
assert format_dict(input_dict, "ChatOpenAI") == expected_output_openai_chat
# Test 8: Replace dict type with str
input_dict = {
"field1": {"type": "Dict[str, int]", "required": False},
}
expected_output = {
"field1": {
"type": "Dict[str, int]",
"required": False,
"list": False,
"show": False,
"password": False,
"multiline": False,
},
}
assert format_dict(input_dict) == expected_output
# Test get_base_classes # Test get_base_classes
def test_get_base_classes(): def test_get_base_classes():
base_classes_parent = get_base_classes(Parent) base_classes_parent = get_base_classes(Parent)