From f8d4bb70ac917898f898e461e47239ab7fad5ba7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 17:45:31 -0300 Subject: [PATCH 001/213] =?UTF-8?q?=F0=9F=93=A6=20chore(frontend):=20add?= =?UTF-8?q?=20.dockerignore=20file=20to=20exclude=20node=5Fmodules=20and?= =?UTF-8?q?=20build=20directories=20from=20Docker=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/.dockerignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/frontend/.dockerignore diff --git a/src/frontend/.dockerignore b/src/frontend/.dockerignore new file mode 100644 index 000000000..ca5762007 --- /dev/null +++ b/src/frontend/.dockerignore @@ -0,0 +1,2 @@ +**/node_modules +**/build \ No newline at end of file From 9fcadcb0a50cc9b6c1786aff45702016abb7e999 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 17:46:02 -0300 Subject: [PATCH 002/213] =?UTF-8?q?=F0=9F=93=A6=20chore(base.Dockerfile):?= =?UTF-8?q?=20add=20base=20Dockerfile=20for=20Python=20project=20setup=20?= =?UTF-8?q?=F0=9F=9A=A7=20feat(base.Dockerfile):=20create=20python-base=20?= =?UTF-8?q?stage=20with=20shared=20environment=20variables=20=F0=9F=9A=A7?= =?UTF-8?q?=20feat(base.Dockerfile):=20create=20builder-base=20stage=20for?= =?UTF-8?q?=20building=20dependencies=20and=20creating=20virtual=20environ?= =?UTF-8?q?ment=20=F0=9F=9A=A7=20feat(base.Dockerfile):=20create=20develop?= =?UTF-8?q?ment=20stage=20for=20development=20and=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.Dockerfile | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 base.Dockerfile diff --git a/base.Dockerfile b/base.Dockerfile new file mode 100644 index 000000000..23d0262b5 --- /dev/null +++ b/base.Dockerfile @@ -0,0 +1,87 @@ + + +# syntax=docker/dockerfile:1 +# Keep this syntax directive! It's used to enable Docker BuildKit + +# Based on https://github.com/python-poetry/poetry/discussions/1879?sort=top#discussioncomment-216865 +# but I try to keep it updated (see history) + +################################ +# PYTHON-BASE +# Sets up all our shared environment variables +################################ +FROM python:3.10-slim as python-base + +# python +ENV PYTHONUNBUFFERED=1 \ + # prevents python creating .pyc files + PYTHONDONTWRITEBYTECODE=1 \ + \ + # pip + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 \ + \ + # poetry + # https://python-poetry.org/docs/configuration/#using-environment-variables + POETRY_VERSION=1.5.1 \ + # make poetry install to this location + POETRY_HOME="/opt/poetry" \ + # make poetry create the virtual environment in the project's root + # it gets named `.venv` + POETRY_VIRTUALENVS_IN_PROJECT=true \ + # do not ask any interactive question + POETRY_NO_INTERACTION=1 \ + \ + # paths + # this is where our requirements + virtual environment will live + PYSETUP_PATH="/opt/pysetup" \ + VENV_PATH="/opt/pysetup/.venv" + + +# prepend poetry and venv to path +ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" + + +################################ +# BUILDER-BASE +# Used to build deps + create our virtual environment +################################ +FROM python-base as builder-base +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + # deps for installing poetry + curl \ + # deps for building python deps + build-essential + +# install poetry - respects $POETRY_VERSION & $POETRY_HOME +# The --mount will mount the buildx cache directory to where +# Poetry and Pip store their cache so that they can re-use it +RUN --mount=type=cache,target=/root/.cache \ + curl -sSL https://install.python-poetry.org | python3 - + +# copy project requirement files here to ensure they will be cached. +WORKDIR $PYSETUP_PATH +COPY poetry.lock pyproject.toml ./ +COPY ./src/backend ./src/backend +# Copy README.md to the build context +COPY README.md . +# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally +RUN --mount=type=cache,target=/root/.cache \ + poetry install --without=dev + + +################################ +# DEVELOPMENT +# Image used during development / testing +################################ +FROM python-base as development +WORKDIR $PYSETUP_PATH + +# copy in our built poetry + venv +COPY --from=builder-base $POETRY_HOME $POETRY_HOME +COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH + +# quicker install as runtime deps are already installed +RUN --mount=type=cache,target=/root/.cache \ + poetry install --with=dev From a1df839dc13d66555fb2da2b6ecf95095f44f62b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 17:48:05 -0300 Subject: [PATCH 003/213] =?UTF-8?q?=F0=9F=90=9B=20fix(dev.Dockerfile):=20u?= =?UTF-8?q?pdate=20uvicorn=20command=20to=20use=20--factory=20flag=20and?= =?UTF-8?q?=20create=5Fapp=20function=20to=20improve=20modularity=20and=20?= =?UTF-8?q?maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev.Dockerfile b/dev.Dockerfile index b38929db2..3fcc0803d 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -15,4 +15,4 @@ COPY ./ ./ # Install dependencies RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi -CMD ["uvicorn", "langflow.main:app", "--host", "0.0.0.0", "--port", "5003", "--reload", "log-level", "debug"] \ No newline at end of file +CMD ["uvicorn","--factory", "langflow.main:create_app", "--host", "0.0.0.0", "--port", "5003", "--reload", "log-level", "debug"] \ No newline at end of file From 62ccab2cb9553c2c88d72efde35e62721db13665 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 17:49:08 -0300 Subject: [PATCH 004/213] =?UTF-8?q?=F0=9F=93=A6=20chore(docker-compose.cel?= =?UTF-8?q?ery.yml):=20add=20docker-compose=20file=20for=20running=20Celer?= =?UTF-8?q?y=20worker=20and=20Flower?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.celery.yml | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docker-compose.celery.yml diff --git a/docker-compose.celery.yml b/docker-compose.celery.yml new file mode 100644 index 000000000..d648b331b --- /dev/null +++ b/docker-compose.celery.yml @@ -0,0 +1,60 @@ +version: "3" + +services: + backend: + build: + context: ./ + dockerfile: base.Dockerfile + ports: + - "7860:7860" + volumes: + - ./:/app + command: bash -c "uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload" + + queue: + image: redis:latest + ports: + - "6379:6379" + + celeryworker: + depends_on: + - queue + env_file: + - .env + # environment: + # - SERVER_NAME=${DOMAIN?Variable not set} + # - SERVER_HOST=https://${DOMAIN?Variable not set} + # # Allow explicit env var override for tests + # - SMTP_HOST=${SMTP_HOST?Variable not set} + build: + context: ./ + dockerfile: base.Dockerfile + command: celery -A langflow.worker.celery_app worker --loglevel=DEBUG + + flower: + networks: + - default + build: + context: ./ + dockerfile: base.Dockerfile + env_file: + - .env + command: celery -A langflow.worker.celery_app flower --port=5555 --broker=redis://queue:6379/0 + ports: + - "5555:5555" + + frontend: + build: + context: ./src/frontend + dockerfile: ./dev.Dockerfile + args: + - BACKEND_URL=http://backend:7860 + environment: + - VITE_PROXY_TARGET=http://backend:7860 + ports: + - "3000:3000" + volumes: + - ./src/frontend/public:/home/node/app/public + - ./src/frontend/src:/home/node/app/src + - ./src/frontend/package.json:/home/node/app/package.json + restart: on-failure From d1a9ed45d772e62c3a8edd57e6b8e3b5d431cb8b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 17:54:53 -0300 Subject: [PATCH 005/213] =?UTF-8?q?=F0=9F=93=A6=20chore(pyproject.toml):?= =?UTF-8?q?=20update=20orjson=20version=20from=203.9.1=20to=203.9.3=20for?= =?UTF-8?q?=20bug=20fixes=20and=20improvements=20=F0=9F=93=A6=20chore(pypr?= =?UTF-8?q?oject.toml):=20add=20celery=20version=205.3.1,=20redis=20versio?= =?UTF-8?q?n=204.6.0,=20and=20flower=20version=202.0.1=20to=20dev=20depend?= =?UTF-8?q?encies=20for=20task=20queue=20and=20monitoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 1056 ++++++++++++++++++++++++++++++------------------ pyproject.toml | 5 +- 2 files changed, 657 insertions(+), 404 deletions(-) diff --git a/poetry.lock b/poetry.lock index 47c471ddc..33107445e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "aiofiles" -version = "23.1.0" +version = "23.2.1" description = "File support for asyncio." optional = false -python-versions = ">=3.7,<4.0" +python-versions = ">=3.7" files = [ - {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, - {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, + {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, + {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, ] [[package]] @@ -144,15 +144,29 @@ files = [ {file = "aiostream-0.4.5.tar.gz", hash = "sha256:3ecbf87085230fbcd9605c32ca20c4fb41af02c71d076eab246ea22e35947d88"}, ] +[[package]] +name = "amqp" +version = "5.1.1" +description = "Low-level AMQP client for Python (fork of amqplib)." +optional = false +python-versions = ">=3.6" +files = [ + {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, + {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, +] + +[package.dependencies] +vine = ">=5.0.0" + [[package]] name = "anthropic" -version = "0.3.8" +version = "0.3.9" description = "Client library for the anthropic API" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "anthropic-0.3.8-py3-none-any.whl", hash = "sha256:97ffe1bacc4214dc89b19f496cf2769746971e86f7c835a05aa21b76f260d279"}, - {file = "anthropic-0.3.8.tar.gz", hash = "sha256:6651099807456c3b95b3879f5ad7d00f7e7e4f7649a2394d18032ab8be54ef16"}, + {file = "anthropic-0.3.9-py3-none-any.whl", hash = "sha256:23e8daf266c707faa0b85328ada03dcf885e62c2f236eb0159352e29b9c4e2e3"}, + {file = "anthropic-0.3.9.tar.gz", hash = "sha256:c19b75308c07cb1ecbea03ffcdde9ba68e5ca22a22479217712396385678bde3"}, ] [package.dependencies] @@ -161,7 +175,7 @@ distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" pydantic = ">=1.9.0,<2.0.0" tokenizers = ">=0.13.0" -typing-extensions = ">=4.1.1,<5" +typing-extensions = ">=4.5,<5" [[package]] name = "anyio" @@ -253,13 +267,13 @@ test = ["astroid", "pytest"] [[package]] name = "async-timeout" -version = "4.0.2" +version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] @@ -334,6 +348,17 @@ soupsieve = ">1.2" html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "billiard" +version = "4.1.0" +description = "Python multiprocessing fork with improvements and bugfixes" +optional = false +python-versions = ">=3.7" +files = [ + {file = "billiard-4.1.0-py3-none-any.whl", hash = "sha256:0f50d6be051c6b2b75bfbc8bfd85af195c5739c281d3f5b86a5640c65563614a"}, + {file = "billiard-4.1.0.tar.gz", hash = "sha256:1ad2eeae8e28053d729ba3373d34d9d6e210f6e4d8bf0a9c64f92bd053f1edf5"}, +] + [[package]] name = "black" version = "23.7.0" @@ -409,6 +434,61 @@ files = [ {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, ] +[[package]] +name = "celery" +version = "5.3.1" +description = "Distributed Task Queue." +optional = false +python-versions = ">=3.8" +files = [ + {file = "celery-5.3.1-py3-none-any.whl", hash = "sha256:27f8f3f3b58de6e0ab4f174791383bbd7445aff0471a43e99cfd77727940753f"}, + {file = "celery-5.3.1.tar.gz", hash = "sha256:f84d1c21a1520c116c2b7d26593926581191435a03aa74b77c941b93ca1c6210"}, +] + +[package.dependencies] +billiard = ">=4.1.0,<5.0" +click = ">=8.1.2,<9.0" +click-didyoumean = ">=0.3.0" +click-plugins = ">=1.1.1" +click-repl = ">=0.2.0" +kombu = ">=5.3.1,<6.0" +python-dateutil = ">=2.8.2" +tzdata = ">=2022.7" +vine = ">=5.0.0,<6.0" + +[package.extras] +arangodb = ["pyArango (>=2.0.1)"] +auth = ["cryptography (==41.0.1)"] +azureblockblob = ["azure-storage-blob (>=12.15.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (>=3.25.0,<4)"] +consul = ["python-consul2 (==0.1.5)"] +cosmosdbsql = ["pydocumentdb (==2.3.5)"] +couchbase = ["couchbase (>=3.0.0)"] +couchdb = ["pycouchdb (==1.14.2)"] +django = ["Django (>=2.2.28)"] +dynamodb = ["boto3 (>=1.26.143)"] +elasticsearch = ["elasticsearch (<8.0)"] +eventlet = ["eventlet (>=0.32.0)"] +gevent = ["gevent (>=1.5.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +memcache = ["pylibmc (==1.6.3)"] +mongodb = ["pymongo[srv] (>=4.0.2)"] +msgpack = ["msgpack (==1.0.5)"] +pymemcache = ["python-memcached (==1.59)"] +pyro = ["pyro4 (==4.82)"] +pytest = ["pytest-celery (==0.0.0)"] +redis = ["redis (>=4.5.2,!=4.5.5)"] +s3 = ["boto3 (>=1.26.143)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem (==4.1.4)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard (==0.21.0)"] + [[package]] name = "certifi" version = "2023.7.22" @@ -634,6 +714,20 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-didyoumean" +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] + +[package.dependencies] +click = ">=7" + [[package]] name = "click-log" version = "0.4.0" @@ -648,6 +742,41 @@ files = [ [package.dependencies] click = "*" +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +optional = false +python-versions = "*" +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "click-repl" +version = "0.3.0" +description = "REPL plugin for Click" +optional = false +python-versions = ">=3.6" +files = [ + {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, + {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, +] + +[package.dependencies] +click = ">=7.0" +prompt-toolkit = ">=3.0.36" + +[package.extras] +testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] + [[package]] name = "clickhouse-connect" version = "0.6.8" @@ -739,13 +868,13 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"] [[package]] name = "cohere" -version = "4.19.2" +version = "4.19.3" description = "" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "cohere-4.19.2-py3-none-any.whl", hash = "sha256:0b6a4fe04380a481a8e975ebcc9bb6433febe4d3eb583b6d6e04342a5e998345"}, - {file = "cohere-4.19.2.tar.gz", hash = "sha256:a0b0fa698b3d3983fb328bb90d68fcf08faaa2268f3772ebc6bfea6ba55acf27"}, + {file = "cohere-4.19.3-py3-none-any.whl", hash = "sha256:6c98f1e58b93b6316c824385c1d2032ed352280e9efa5695ba98306258abf84f"}, + {file = "cohere-4.19.3.tar.gz", hash = "sha256:c3aaa716c4da7d7a8ed68705fcdc92f1b1a2260b737cee6bd27af5c347f31496"}, ] [package.dependencies] @@ -805,71 +934,63 @@ typing = ["mypy (>=0.990)"] [[package]] name = "coverage" -version = "7.2.7" +version = "7.3.0" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, - {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, - {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, - {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, - {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, - {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, - {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, - {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, - {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, - {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, - {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, - {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, - {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, - {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, - {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, - {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, - {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, - {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, + {file = "coverage-7.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5"}, + {file = "coverage-7.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637"}, + {file = "coverage-7.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af"}, + {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1"}, + {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12"}, + {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689"}, + {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977"}, + {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51"}, + {file = "coverage-7.3.0-cp310-cp310-win32.whl", hash = "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527"}, + {file = "coverage-7.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1"}, + {file = "coverage-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f"}, + {file = "coverage-7.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d"}, + {file = "coverage-7.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd"}, + {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7"}, + {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a"}, + {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74"}, + {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214"}, + {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f"}, + {file = "coverage-7.3.0-cp311-cp311-win32.whl", hash = "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482"}, + {file = "coverage-7.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70"}, + {file = "coverage-7.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b"}, + {file = "coverage-7.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446"}, + {file = "coverage-7.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071"}, + {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe"}, + {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a"}, + {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873"}, + {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2"}, + {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b"}, + {file = "coverage-7.3.0-cp312-cp312-win32.whl", hash = "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321"}, + {file = "coverage-7.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479"}, + {file = "coverage-7.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1"}, + {file = "coverage-7.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd"}, + {file = "coverage-7.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e"}, + {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54"}, + {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254"}, + {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0"}, + {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84"}, + {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985"}, + {file = "coverage-7.3.0-cp38-cp38-win32.whl", hash = "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9"}, + {file = "coverage-7.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543"}, + {file = "coverage-7.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba"}, + {file = "coverage-7.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393"}, + {file = "coverage-7.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28"}, + {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95"}, + {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a"}, + {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34"}, + {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e"}, + {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54"}, + {file = "coverage-7.3.0-cp39-cp39-win32.whl", hash = "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3"}, + {file = "coverage-7.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e"}, + {file = "coverage-7.3.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0"}, + {file = "coverage-7.3.0.tar.gz", hash = "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865"}, ] [package.dependencies] @@ -925,13 +1046,13 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "ctransformers" -version = "0.2.21" +version = "0.2.22" description = "Python bindings for the Transformer models implemented in C/C++ using GGML library." optional = true python-versions = "*" files = [ - {file = "ctransformers-0.2.21-py3-none-any.whl", hash = "sha256:18a0555d02f55a3935f5544b885038562f80e497a6197d8e871941a087dba546"}, - {file = "ctransformers-0.2.21.tar.gz", hash = "sha256:58e7a699050a106688b967faa59f377886e22a581fde6cd36821dfa541995677"}, + {file = "ctransformers-0.2.22-py3-none-any.whl", hash = "sha256:cce72b4ffff3f29d49ea6488110686453b3354c285e96b9c7dd16f273c4b4fc4"}, + {file = "ctransformers-0.2.22.tar.gz", hash = "sha256:ffd15dfe8a6ad45568ac0423bbbb13d7d71b680a7d0b59871a193df8a4b8fdca"}, ] [package.dependencies] @@ -960,29 +1081,29 @@ typing-inspect = ">=0.4.0,<1" [[package]] name = "debugpy" -version = "1.6.7" +version = "1.6.7.post1" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.7" files = [ - {file = "debugpy-1.6.7-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b3e7ac809b991006ad7f857f016fa92014445085711ef111fdc3f74f66144096"}, - {file = "debugpy-1.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3876611d114a18aafef6383695dfc3f1217c98a9168c1aaf1a02b01ec7d8d1e"}, - {file = "debugpy-1.6.7-cp310-cp310-win32.whl", hash = "sha256:33edb4afa85c098c24cc361d72ba7c21bb92f501104514d4ffec1fb36e09c01a"}, - {file = "debugpy-1.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:ed6d5413474e209ba50b1a75b2d9eecf64d41e6e4501977991cdc755dc83ab0f"}, - {file = "debugpy-1.6.7-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:38ed626353e7c63f4b11efad659be04c23de2b0d15efff77b60e4740ea685d07"}, - {file = "debugpy-1.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279d64c408c60431c8ee832dfd9ace7c396984fd7341fa3116aee414e7dcd88d"}, - {file = "debugpy-1.6.7-cp37-cp37m-win32.whl", hash = "sha256:dbe04e7568aa69361a5b4c47b4493d5680bfa3a911d1e105fbea1b1f23f3eb45"}, - {file = "debugpy-1.6.7-cp37-cp37m-win_amd64.whl", hash = "sha256:f90a2d4ad9a035cee7331c06a4cf2245e38bd7c89554fe3b616d90ab8aab89cc"}, - {file = "debugpy-1.6.7-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:5224eabbbeddcf1943d4e2821876f3e5d7d383f27390b82da5d9558fd4eb30a9"}, - {file = "debugpy-1.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae1123dff5bfe548ba1683eb972329ba6d646c3a80e6b4c06cd1b1dd0205e9b"}, - {file = "debugpy-1.6.7-cp38-cp38-win32.whl", hash = "sha256:9cd10cf338e0907fdcf9eac9087faa30f150ef5445af5a545d307055141dd7a4"}, - {file = "debugpy-1.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:aaf6da50377ff4056c8ed470da24632b42e4087bc826845daad7af211e00faad"}, - {file = "debugpy-1.6.7-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:0679b7e1e3523bd7d7869447ec67b59728675aadfc038550a63a362b63029d2c"}, - {file = "debugpy-1.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de86029696e1b3b4d0d49076b9eba606c226e33ae312a57a46dca14ff370894d"}, - {file = "debugpy-1.6.7-cp39-cp39-win32.whl", hash = "sha256:d71b31117779d9a90b745720c0eab54ae1da76d5b38c8026c654f4a066b0130a"}, - {file = "debugpy-1.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:c0ff93ae90a03b06d85b2c529eca51ab15457868a377c4cc40a23ab0e4e552a3"}, - {file = "debugpy-1.6.7-py2.py3-none-any.whl", hash = "sha256:53f7a456bc50706a0eaabecf2d3ce44c4d5010e46dfc65b6b81a518b42866267"}, - {file = "debugpy-1.6.7.zip", hash = "sha256:c4c2f0810fa25323abfdfa36cbbbb24e5c3b1a42cb762782de64439c575d67f2"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:903bd61d5eb433b6c25b48eae5e23821d4c1a19e25c9610205f5aeaccae64e32"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16882030860081e7dd5aa619f30dec3c2f9a421e69861125f83cc372c94e57d"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-win32.whl", hash = "sha256:eea8d8cfb9965ac41b99a61f8e755a8f50e9a20330938ad8271530210f54e09c"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-win_amd64.whl", hash = "sha256:85969d864c45f70c3996067cfa76a319bae749b04171f2cdeceebe4add316155"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:890f7ab9a683886a0f185786ffbda3b46495c4b929dab083b8c79d6825832a52"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4ac7a4dba28801d184b7fc0e024da2635ca87d8b0a825c6087bb5168e3c0d28"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-win32.whl", hash = "sha256:3370ef1b9951d15799ef7af41f8174194f3482ee689988379763ef61a5456426"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:65b28435a17cba4c09e739621173ff90c515f7b9e8ea469b92e3c28ef8e5cdfb"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:92b6dae8bfbd497c90596bbb69089acf7954164aea3228a99d7e43e5267f5b36"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72f5d2ecead8125cf669e62784ef1e6300f4067b0f14d9f95ee00ae06fc7c4f7"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-win32.whl", hash = "sha256:f0851403030f3975d6e2eaa4abf73232ab90b98f041e3c09ba33be2beda43fcf"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-win_amd64.whl", hash = "sha256:3de5d0f97c425dc49bce4293df6a04494309eedadd2b52c22e58d95107e178d9"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:38651c3639a4e8bbf0ca7e52d799f6abd07d622a193c406be375da4d510d968d"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:038c51268367c9c935905a90b1c2d2dbfe304037c27ba9d19fe7409f8cdc710c"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-win32.whl", hash = "sha256:4b9eba71c290852f959d2cf8a03af28afd3ca639ad374d393d53d367f7f685b2"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-win_amd64.whl", hash = "sha256:973a97ed3b434eab0f792719a484566c35328196540676685c975651266fccf9"}, + {file = "debugpy-1.6.7.post1-py2.py3-none-any.whl", hash = "sha256:1093a5c541af079c13ac8c70ab8b24d1d35c8cacb676306cf11e57f699c02926"}, + {file = "debugpy-1.6.7.post1.zip", hash = "sha256:fe87ec0182ef624855d05e6ed7e0b7cb1359d2ffa2a925f8ec2d22e98b75d0ca"}, ] [[package]] @@ -1065,13 +1186,13 @@ files = [ [[package]] name = "dnspython" -version = "2.4.1" +version = "2.4.2" description = "DNS toolkit" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "dnspython-2.4.1-py3-none-any.whl", hash = "sha256:5b7488477388b8c0b70a8ce93b227c5603bc7b77f1565afe8e729c36c51447d7"}, - {file = "dnspython-2.4.1.tar.gz", hash = "sha256:c33971c79af5be968bb897e95c2448e11a645ee84d93b265ce0b7aabe5dfdca8"}, + {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, + {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, ] [package.extras] @@ -1426,6 +1547,24 @@ files = [ {file = "flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89"}, ] +[[package]] +name = "flower" +version = "2.0.1" +description = "Celery Flower" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flower-2.0.1-py2.py3-none-any.whl", hash = "sha256:9db2c621eeefbc844c8dd88be64aef61e84e2deb29b271e02ab2b5b9f01068e2"}, + {file = "flower-2.0.1.tar.gz", hash = "sha256:5ab717b979530770c16afb48b50d2a98d23c3e9fe39851dcf6bc4d01845a02a0"}, +] + +[package.dependencies] +celery = ">=5.0.5" +humanize = "*" +prometheus-client = ">=0.8.0" +pytz = "*" +tornado = ">=5.0.0,<7.0.0" + [[package]] name = "frozenlist" version = "1.4.0" @@ -1585,13 +1724,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.95.0" +version = "2.96.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-python-client-2.95.0.tar.gz", hash = "sha256:d2731ede12f79e53fbe11fdb913dfe986440b44c0a28431c78a8ec275f4c1541"}, - {file = "google_api_python_client-2.95.0-py2.py3-none-any.whl", hash = "sha256:a8aab2da678f42a01f2f52108f787fef4310f23f9dd917c4e64664c3f0c885ba"}, + {file = "google-api-python-client-2.96.0.tar.gz", hash = "sha256:f712373d03d338af57b9f5fe98c91f4b5baaa8765469b015bc623c4681c5bd51"}, + {file = "google_api_python_client-2.96.0-py2.py3-none-any.whl", hash = "sha256:38c2b61b10d15bb41ec8f89303e3837ec2d2c3e4e38de5800c05ee322492f937"}, ] [package.dependencies] @@ -1644,13 +1783,13 @@ six = "*" [[package]] name = "google-cloud-aiplatform" -version = "1.29.0" +version = "1.30.1" description = "Vertex AI API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-aiplatform-1.29.0.tar.gz", hash = "sha256:fceabb924d2d26057e3c8c5c2e251929389aa6d553361377bc402781150c0db3"}, - {file = "google_cloud_aiplatform-1.29.0-py2.py3-none-any.whl", hash = "sha256:cf81c1d93c61ccf3df60a65e3a5a1e465e044059d36b6fc1202b940c46c4c1e1"}, + {file = "google-cloud-aiplatform-1.30.1.tar.gz", hash = "sha256:7552a6b2e66d7a9ff3c4b2bb95b0e9c182e7475dfb35d6347e9299f78779135a"}, + {file = "google_cloud_aiplatform-1.30.1-py2.py3-none-any.whl", hash = "sha256:ab1bbd4cf83cf583b7dea7e53421ad076f18b63e93cb22fb53c03176d5aa9258"}, ] [package.dependencies] @@ -2391,6 +2530,20 @@ files = [ [package.dependencies] pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} +[[package]] +name = "humanize" +version = "4.7.0" +description = "Python humanize utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "humanize-4.7.0-py3-none-any.whl", hash = "sha256:df7c429c2d27372b249d3f26eb53b07b166b661326e0325793e0a988082e3889"}, + {file = "humanize-4.7.0.tar.gz", hash = "sha256:7ca0e43e870981fa684acb5b062deb307218193bca1a01f2b2676479df849b3a"}, +] + +[package.extras] +tests = ["freezegun", "pytest", "pytest-cov"] + [[package]] name = "hyperframe" version = "6.0.1" @@ -2779,13 +2932,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "joblib" -version = "1.3.1" +version = "1.3.2" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.7" files = [ - {file = "joblib-1.3.1-py3-none-any.whl", hash = "sha256:89cf0529520e01b3de7ac7b74a8102c90d16d54c64b5dd98cafcd14307fdf915"}, - {file = "joblib-1.3.1.tar.gz", hash = "sha256:1f937906df65329ba98013dc9692fe22a4c5e4a648112de500508b18a21b41e3"}, + {file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"}, + {file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"}, ] [[package]] @@ -2854,6 +3007,39 @@ completion = ["shtab"] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +[[package]] +name = "kombu" +version = "5.3.1" +description = "Messaging library for Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "kombu-5.3.1-py3-none-any.whl", hash = "sha256:48ee589e8833126fd01ceaa08f8a2041334e9f5894e5763c8486a550454551e9"}, + {file = "kombu-5.3.1.tar.gz", hash = "sha256:fbd7572d92c0bf71c112a6b45163153dea5a7b6a701ec16b568c27d0fd2370f2"}, +] + +[package.dependencies] +amqp = ">=5.1.1,<6.0.0" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.10.0)"] +azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] +confluentkafka = ["confluent-kafka (==2.1.1)"] +consul = ["python-consul2"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=4.1.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=4.5.2)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=2.8.0)"] + [[package]] name = "langchain" version = "0.0.256" @@ -2909,12 +3095,12 @@ langchain = ">=0.0.239" [[package]] name = "langchain-serve" -version = "0.0.59" +version = "0.0.60" description = "Langchain Serve - serve your langchain apps on Jina AI Cloud." optional = true python-versions = "*" files = [ - {file = "langchain-serve-0.0.59.tar.gz", hash = "sha256:28ff4ba1b640223158413296ac5b9198eac7cbd206d3386855ad00066a9f91d6"}, + {file = "langchain-serve-0.0.60.tar.gz", hash = "sha256:bfcc2e7c2a3cd3b4cde5ff45043cc9c8d437704941b02d166185d8334a120561"}, ] [package.dependencies] @@ -2934,17 +3120,17 @@ test = ["psutil", "pytest", "pytest-asyncio"] [[package]] name = "langsmith" -version = "0.0.19" +version = "0.0.22" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langsmith-0.0.19-py3-none-any.whl", hash = "sha256:ae240030fd0b98e9467fbf19ac6d58a0a4ffcc1db8462625141dae6178e62c68"}, - {file = "langsmith-0.0.19.tar.gz", hash = "sha256:e91a2cd101456e2f8d6015c9ea371d6556eb6072a1b20d4793479855163ae28f"}, + {file = "langsmith-0.0.22-py3-none-any.whl", hash = "sha256:1bc94a2e5bfa355ca15d9e658c2c2d04c8cc45c61892a1be08a7c3b40f2fd3f4"}, + {file = "langsmith-0.0.22.tar.gz", hash = "sha256:5726c7841294db2a9e5863e20718878d16e28722bdaf3169a278ff3bda2f0be7"}, ] [package.dependencies] -pydantic = ">=1,<2" +pydantic = ">=1,<3" requests = ">=2,<3" [[package]] @@ -3484,37 +3670,33 @@ dill = ">=0.3.7" [[package]] name = "mypy" -version = "1.4.1" +version = "1.5.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, - {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, - {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, - {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, - {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, - {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, - {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, - {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, - {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, - {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, - {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, - {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, - {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, - {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, - {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, - {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, - {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, - {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, - {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, - {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, + {file = "mypy-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ad3109bec37cc33654de8db30fe8ff3a1bb57ea65144167d68185e6dced9868d"}, + {file = "mypy-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b4ea3a0241cb005b0ccdbd318fb99619b21ae51bcf1660b95fc22e0e7d3ba4a1"}, + {file = "mypy-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fe816e26e676c1311b9e04fd576543b873576d39439f7c24c8e5c7728391ecf"}, + {file = "mypy-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42170e68adb1603ccdc55a30068f72bcfcde2ce650188e4c1b2a93018b826735"}, + {file = "mypy-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d145b81a8214687cfc1f85c03663a5bbe736777410e5580e54d526e7e904f564"}, + {file = "mypy-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c36011320e452eb30bec38b9fd3ba20569dc9545d7d4540d967f3ea1fab9c374"}, + {file = "mypy-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3940cf5845b2512b3ab95463198b0cdf87975dfd17fdcc6ce9709a9abe09e69"}, + {file = "mypy-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9166186c498170e1ff478a7f540846b2169243feb95bc228d39a67a1a450cdc6"}, + {file = "mypy-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:725b57a19b7408ef66a0fd9db59b5d3e528922250fb56e50bded27fea9ff28f0"}, + {file = "mypy-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:eec5c927aa4b3e8b4781840f1550079969926d0a22ce38075f6cfcf4b13e3eb4"}, + {file = "mypy-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79c520aa24f21852206b5ff2cf746dc13020113aa73fa55af504635a96e62718"}, + {file = "mypy-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:769ddb6bfe55c2bd9c7d6d7020885a5ea14289619db7ee650e06b1ef0852c6f4"}, + {file = "mypy-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf18f8db7e5f060d61c91e334d3b96d6bb624ddc9ee8a1cde407b737acbca2c"}, + {file = "mypy-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a2500ad063413bc873ae102cf655bf49889e0763b260a3a7cf544a0cbbf7e70a"}, + {file = "mypy-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:84cf9f7d8a8a22bb6a36444480f4cbf089c917a4179fbf7eea003ea931944a7f"}, + {file = "mypy-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a551ed0fc02455fe2c1fb0145160df8336b90ab80224739627b15ebe2b45e9dc"}, + {file = "mypy-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:372fd97293ed0076d52695849f59acbbb8461c4ab447858cdaeaf734a396d823"}, + {file = "mypy-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8a7444d6fcac7e2585b10abb91ad900a576da7af8f5cffffbff6065d9115813"}, + {file = "mypy-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:35b13335c6c46a386577a51f3d38b2b5d14aa619e9633bb756bd77205e4bd09f"}, + {file = "mypy-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:2c9d570f53908cbea326ad8f96028a673b814d9dca7515bf71d95fa662c3eb6f"}, + {file = "mypy-1.5.0-py3-none-any.whl", hash = "sha256:69b32d0dedd211b80f1b7435644e1ef83033a2af2ac65adcdc87c38db68a86be"}, + {file = "mypy-1.5.0.tar.gz", hash = "sha256:f3460f34b3839b9bc84ee3ed65076eb827cd99ed13ed08d723f9083cada4a212"}, ] [package.dependencies] @@ -3525,7 +3707,6 @@ typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] [[package]] @@ -4036,28 +4217,67 @@ files = [ [[package]] name = "orjson" -version = "3.9.4" +version = "3.9.3" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.7" files = [ - {file = "orjson-3.9.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2e83ec1ee66d83b558a6d273d8a01b86563daa60bea9bc040e2c1cb8008de61f"}, - {file = "orjson-3.9.4-cp310-none-win32.whl", hash = "sha256:04cd7f4a4f4cd2fe43d104eb70e7435c6fcbdde7aa0cde4230e444fbc66924d3"}, - {file = "orjson-3.9.4-cp310-none-win_amd64.whl", hash = "sha256:4fdb59cfa00e10c82e09d1c32a9ce08a38bd29496ba20a73cd7f498e3a0a5024"}, - {file = "orjson-3.9.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:daeed2502ddf1f2b29ec8da2fe2ea82807a5c4acf869608ce6c476db8171d070"}, - {file = "orjson-3.9.4-cp311-none-win32.whl", hash = "sha256:e12492ce65cb10f385e70a88badc6046bc720fa7d468db27b7429d85d41beaeb"}, - {file = "orjson-3.9.4-cp311-none-win_amd64.whl", hash = "sha256:3b9f8bf43a5367d5522f80e7d533c98d880868cd0b640b9088c9237306eca6e8"}, - {file = "orjson-3.9.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0b400cf89c15958cd829c8a4ade8f5dd73588e63d2fb71a00483e7a74e9f92da"}, - {file = "orjson-3.9.4-cp312-none-win_amd64.whl", hash = "sha256:a533e664a0e3904307d662c5d45775544dc2b38df6e39e213ff6a86ceaa3d53c"}, - {file = "orjson-3.9.4-cp37-cp37m-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:149d1b7630771222f73ecb024ab5dd8e7f41502402b02015494d429bacc4d5c1"}, - {file = "orjson-3.9.4-cp37-none-win32.whl", hash = "sha256:bcda6179eb863c295eb5ea832676d33ef12c04d227b4c98267876c8322e5a96e"}, - {file = "orjson-3.9.4-cp37-none-win_amd64.whl", hash = "sha256:3d947366127abef192419257eb7db7fcee0841ced2b49ccceba43b65e9ce5e3f"}, - {file = "orjson-3.9.4-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a7d029fc34a516f7eae29b778b30371fcb621134b2acfe4c51c785102aefc6cf"}, - {file = "orjson-3.9.4-cp38-none-win32.whl", hash = "sha256:94d15ee45c2aaed334688e511aa73b4681f7c08a0810884c6b3ae5824dea1222"}, - {file = "orjson-3.9.4-cp38-none-win_amd64.whl", hash = "sha256:336ec8471102851f0699198031924617b7a77baadea889df3ffda6000bd59f4c"}, - {file = "orjson-3.9.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2f57ccb50e9e123709e9f2d7b1a9e09e694e49d1fa5c5585e34b8e3f01929dc3"}, - {file = "orjson-3.9.4-cp39-none-win32.whl", hash = "sha256:b5b5038187b74e2d33e5caee8a7e83ddeb6a21da86837fa2aac95c69aeb366e6"}, - {file = "orjson-3.9.4-cp39-none-win_amd64.whl", hash = "sha256:915da36bc93ef0c659fa50fe7939d4f208804ad252fc4fc8d55adbbb82293c48"}, + {file = "orjson-3.9.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:082714b5554fcced092c45272f22a93400389733083c43f5043c4316e86f57a2"}, + {file = "orjson-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97ddec69ca4fa1b66d512cf4f4a3fe6a57c4bf21209295ab2f4ada415996e08a"}, + {file = "orjson-3.9.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab7501722ec2172b1c6ea333bc47bba3bbb9b5fc0e3e891191e8447f43d3187d"}, + {file = "orjson-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ae680163ab09f04683d35fbd63eee858019f0066640f7cbad4dba3e7422a4bc"}, + {file = "orjson-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e5abca1e0a9d110bab7346fab0acd3b7848d2ee13318bc24a31bbfbdad974b8"}, + {file = "orjson-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c55f42a8b07cdb7d514cfaeb56f6e9029eef1cbc8e670ac31fc377c46b993cd1"}, + {file = "orjson-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:303f1324f5ea516f8e874ea0f8d15c581caabdca59fc990705fc76f3bd9f3bdf"}, + {file = "orjson-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c444e3931ea4fe7dec26d195486a681fedc0233230c9b84848f8e60affd4a4"}, + {file = "orjson-3.9.3-cp310-none-win32.whl", hash = "sha256:63333de96d83091023c9c99cc579973a2977b15feb5cdc8d9660104c886e9ab8"}, + {file = "orjson-3.9.3-cp310-none-win_amd64.whl", hash = "sha256:7bce6ff507a83c6a4b6b00726f3a7d7aed0b1f0884aac0440e95b55cac0b113e"}, + {file = "orjson-3.9.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec4421f377cce51decd6ea3869a8b41e9f05c50bf6acef8284f8906e642992c4"}, + {file = "orjson-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b3177bd67756e53bdbd72c79fae3507796a67b67c32a16f4b55cad48ef25c13"}, + {file = "orjson-3.9.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b21908252c8a13b8f48d4cccdb7fabb592824cf39c9fa4e9076015dd65eabeba"}, + {file = "orjson-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7b795c6ac344b0c49776b7e135a9bed0cd15b1ade2a4c7b3a19e3913247702e"}, + {file = "orjson-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac43842f5ba26e6f21b4e63312bd1137111a9b9821d7f7dfe189a4015c6c6bc"}, + {file = "orjson-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8def4f6560c7b6dbc4b356dfd8e6624a018d920ce5a2864291a2bf1052cd6b68"}, + {file = "orjson-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bbc0dafd1de42c8dbfd6e5d1fe4deab15d2de474e11475921286bebefd109ec8"}, + {file = "orjson-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:85b1870d5420292419b34002659082d77f31b13d4d8cbd67bed9d717c775a0fb"}, + {file = "orjson-3.9.3-cp311-none-win32.whl", hash = "sha256:d6ece3f48f14a06c325181f2b9bd9a9827aac2ecdcad11eb12f561fb697eaaaa"}, + {file = "orjson-3.9.3-cp311-none-win_amd64.whl", hash = "sha256:448feda092c681c0a5b8eec62dd4f625ad5d316dafd56c81fb3f05b5221827ff"}, + {file = "orjson-3.9.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:413d7cf731f1222373360128a3d5232d52630a7355f446bf2659fc3445ec0b76"}, + {file = "orjson-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009a0f79804c604998b068f5f942e40546913ed45ee2f0a3d0e75695bf7543fa"}, + {file = "orjson-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ce062844255cce4d6a8a150e8e78b9fcd6c5a3f1ff3f8792922de25827c25b9c"}, + {file = "orjson-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:776659e18debe5de73c30b0957cd6454fcc61d87377fcb276441fca1b9f1305d"}, + {file = "orjson-3.9.3-cp312-none-win_amd64.whl", hash = "sha256:47b237da3818c8e546df4d2162f0a5cfd50b7b58528907919a27244141e0e48e"}, + {file = "orjson-3.9.3-cp37-cp37m-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f954115d8496d4ab5975438e3ce07780c1644ea0a66c78a943ef79f33769b61a"}, + {file = "orjson-3.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c57100517b6dbfe34181ed2248bebfab03bd2a7aafb6fbf849c6fd3bb2fbda"}, + {file = "orjson-3.9.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa6017140fe487ab8fae605a2890c94c6fbe7a8e763ff33bbdb00e27ce078cfd"}, + {file = "orjson-3.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fe77af2ff33c370fb06c9fdf004a66d85ea19c77f0273bbf70c70f98f832725"}, + {file = "orjson-3.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2fa8c385b27bab886caa098fa3ae114d56571ae6e7a5610cb624d7b0a66faed"}, + {file = "orjson-3.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8323739e7905ae4ec4dbdebb31067d28be981f30c11b6ae88ddec2671c0b3194"}, + {file = "orjson-3.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ad43fd5b1ededb54fe01e67468710fcfec8a5830e4ce131f85e741ea151a18e9"}, + {file = "orjson-3.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:42cb645780f732c829bc351346a54157d57f2bc409e671ee36b9fc1037bb77fe"}, + {file = "orjson-3.9.3-cp37-none-win32.whl", hash = "sha256:b84542669d1b0175dc2870025b73cbd4f4a3beb17796de6ec82683663e0400f3"}, + {file = "orjson-3.9.3-cp37-none-win_amd64.whl", hash = "sha256:1440a404ce84f43e2f8e97d8b5fe6f271458e0ffd37290dc3a9f6aa067c69930"}, + {file = "orjson-3.9.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1da8edaefb75f25b449ed4e22d00b9b49211b97dcefd44b742bdd8721d572788"}, + {file = "orjson-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47210746acda49febe3bb07253eb5d63d7c7511beec5fa702aad3ce64e15664f"}, + {file = "orjson-3.9.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:893c62afd5b26f04e2814dffa4d9d4060583ac43dc3e79ed3eadf62a5ac37b2c"}, + {file = "orjson-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32aef33ae33901c327fd5679f91fa37199834d122dffd234416a6fe4193d1982"}, + {file = "orjson-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd2761384ddb9de63b20795845d5cedadf052255a34c3ff1750cfc77b29d9926"}, + {file = "orjson-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e2502b4af2055050dcc74718f2647b65102087c6f5b3f939e2e1a3e3099602"}, + {file = "orjson-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fa7c7a39eeb8dd171f59d96fd4610f908ac14b2f2eb268f4498e5f310bda8da7"}, + {file = "orjson-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc3fe0c0ae7acf00d827efe2506131f1b19af3c87e3d76b0e081748984e51c26"}, + {file = "orjson-3.9.3-cp38-none-win32.whl", hash = "sha256:5b1ff8e920518753b310034e5796f0116f7732b0b27531012d46f0b54f3c8c85"}, + {file = "orjson-3.9.3-cp38-none-win_amd64.whl", hash = "sha256:9f2b1007174c93dd838f52e623c972df33057e3cb7ad9341b7d9bbd66b8d8fb4"}, + {file = "orjson-3.9.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cddc5b8bd7b0d1dfd36637eedbd83726b8b8a5969d3ecee70a9b54a94b8a0258"}, + {file = "orjson-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c3bbf4b6f94fad2fd73c81293da8b343fbd07ce48d7836c07d0d54b58c8e93"}, + {file = "orjson-3.9.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a5cc22ef6973992db18952f8b978781e19a0c62c098f475db936284df9311df7"}, + {file = "orjson-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dcea93630986209c690f27f32398956b04ccbba8f1fa7c3d1bb88a01d9ab87a"}, + {file = "orjson-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:526cb34e63faaad908c34597294507b7a4b999a436b4f206bc4e60ff4e911c20"}, + {file = "orjson-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f5ac6e30ee10af57f52e72f9c8b9bc4846a9343449d10ca2ae9760615da3042"}, + {file = "orjson-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6c37ab097c062bdf535105c7156839c4e370065c476bb2393149ad31a2cdf6e"}, + {file = "orjson-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:27d69628f449c52a7a34836b15ec948804254f7954457f88de53f2f4de99512f"}, + {file = "orjson-3.9.3-cp39-none-win32.whl", hash = "sha256:5297463d8831c2327ed22bf92eb6d50347071ff1c73fb4702d50b8bc514aeac9"}, + {file = "orjson-3.9.3-cp39-none-win_amd64.whl", hash = "sha256:69a33486b5b6e5a99939fdb13c1c0d8bcc7c89fe6083e7b9ce3c70931ca9fb71"}, + {file = "orjson-3.9.3.tar.gz", hash = "sha256:d3da4faf6398154c1e75d32778035fa7dc284814809f76e8f8d50c4f54859399"}, ] [[package]] @@ -5043,13 +5263,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pypdf" -version = "3.15.0" +version = "3.15.1" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.6" files = [ - {file = "pypdf-3.15.0-py3-none-any.whl", hash = "sha256:2e29ddb62561ec91157c784783714703ddd3ce08f070ecbc57404fb86cd9fc97"}, - {file = "pypdf-3.15.0.tar.gz", hash = "sha256:8a6264e1c47c63dc2484e29bdfa76b121435896a84e94b7c5ae82c6ae96354bb"}, + {file = "pypdf-3.15.1-py3-none-any.whl", hash = "sha256:99b337af7da8046d1e2e94354846e8c56753e1cdc817ac0fbe770c1e2281902b"}, + {file = "pypdf-3.15.1.tar.gz", hash = "sha256:d0dfaf4f10dfb06ac39e1d6a9cbffd63e77621d1e89c0ef08f346fd902df7b4b"}, ] [package.dependencies] @@ -5374,88 +5594,104 @@ files = [ [[package]] name = "pyzmq" -version = "25.1.0" +version = "25.1.1" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.6" files = [ - {file = "pyzmq-25.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1a6169e69034eaa06823da6a93a7739ff38716142b3596c180363dee729d713d"}, - {file = "pyzmq-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:19d0383b1f18411d137d891cab567de9afa609b214de68b86e20173dc624c101"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1e931d9a92f628858a50f5bdffdfcf839aebe388b82f9d2ccd5d22a38a789dc"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97d984b1b2f574bc1bb58296d3c0b64b10e95e7026f8716ed6c0b86d4679843f"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:154bddda2a351161474b36dba03bf1463377ec226a13458725183e508840df89"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cb6d161ae94fb35bb518b74bb06b7293299c15ba3bc099dccd6a5b7ae589aee3"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:90146ab578931e0e2826ee39d0c948d0ea72734378f1898939d18bc9c823fcf9"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:831ba20b660b39e39e5ac8603e8193f8fce1ee03a42c84ade89c36a251449d80"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a522510e3434e12aff80187144c6df556bb06fe6b9d01b2ecfbd2b5bfa5c60c"}, - {file = "pyzmq-25.1.0-cp310-cp310-win32.whl", hash = "sha256:be24a5867b8e3b9dd5c241de359a9a5217698ff616ac2daa47713ba2ebe30ad1"}, - {file = "pyzmq-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:5693dcc4f163481cf79e98cf2d7995c60e43809e325b77a7748d8024b1b7bcba"}, - {file = "pyzmq-25.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:13bbe36da3f8aaf2b7ec12696253c0bf6ffe05f4507985a8844a1081db6ec22d"}, - {file = "pyzmq-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:69511d604368f3dc58d4be1b0bad99b61ee92b44afe1cd9b7bd8c5e34ea8248a"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a983c8694667fd76d793ada77fd36c8317e76aa66eec75be2653cef2ea72883"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:332616f95eb400492103ab9d542b69d5f0ff628b23129a4bc0a2fd48da6e4e0b"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58416db767787aedbfd57116714aad6c9ce57215ffa1c3758a52403f7c68cff5"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cad9545f5801a125f162d09ec9b724b7ad9b6440151b89645241d0120e119dcc"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d6128d431b8dfa888bf51c22a04d48bcb3d64431caf02b3cb943269f17fd2994"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b15247c49d8cbea695b321ae5478d47cffd496a2ec5ef47131a9e79ddd7e46c"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:442d3efc77ca4d35bee3547a8e08e8d4bb88dadb54a8377014938ba98d2e074a"}, - {file = "pyzmq-25.1.0-cp311-cp311-win32.whl", hash = "sha256:65346f507a815a731092421d0d7d60ed551a80d9b75e8b684307d435a5597425"}, - {file = "pyzmq-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8b45d722046fea5a5694cba5d86f21f78f0052b40a4bbbbf60128ac55bfcc7b6"}, - {file = "pyzmq-25.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f45808eda8b1d71308c5416ef3abe958f033fdbb356984fabbfc7887bed76b3f"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b697774ea8273e3c0460cf0bba16cd85ca6c46dfe8b303211816d68c492e132"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b324fa769577fc2c8f5efcd429cef5acbc17d63fe15ed16d6dcbac2c5eb00849"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5873d6a60b778848ce23b6c0ac26c39e48969823882f607516b91fb323ce80e5"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f0d9e7ba6a815a12c8575ba7887da4b72483e4cfc57179af10c9b937f3f9308f"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:414b8beec76521358b49170db7b9967d6974bdfc3297f47f7d23edec37329b00"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:01f06f33e12497dca86353c354461f75275a5ad9eaea181ac0dc1662da8074fa"}, - {file = "pyzmq-25.1.0-cp36-cp36m-win32.whl", hash = "sha256:b5a07c4f29bf7cb0164664ef87e4aa25435dcc1f818d29842118b0ac1eb8e2b5"}, - {file = "pyzmq-25.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:968b0c737797c1809ec602e082cb63e9824ff2329275336bb88bd71591e94a90"}, - {file = "pyzmq-25.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:47b915ba666c51391836d7ed9a745926b22c434efa76c119f77bcffa64d2c50c"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af31493663cf76dd36b00dafbc839e83bbca8a0662931e11816d75f36155897"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5489738a692bc7ee9a0a7765979c8a572520d616d12d949eaffc6e061b82b4d1"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1fc56a0221bdf67cfa94ef2d6ce5513a3d209c3dfd21fed4d4e87eca1822e3a3"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:75217e83faea9edbc29516fc90c817bc40c6b21a5771ecb53e868e45594826b0"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3830be8826639d801de9053cf86350ed6742c4321ba4236e4b5568528d7bfed7"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3575699d7fd7c9b2108bc1c6128641a9a825a58577775ada26c02eb29e09c517"}, - {file = "pyzmq-25.1.0-cp37-cp37m-win32.whl", hash = "sha256:95bd3a998d8c68b76679f6b18f520904af5204f089beebb7b0301d97704634dd"}, - {file = "pyzmq-25.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:dbc466744a2db4b7ca05589f21ae1a35066afada2f803f92369f5877c100ef62"}, - {file = "pyzmq-25.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:3bed53f7218490c68f0e82a29c92335daa9606216e51c64f37b48eb78f1281f4"}, - {file = "pyzmq-25.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb52e826d16c09ef87132c6e360e1879c984f19a4f62d8a935345deac43f3c12"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ddbef8b53cd16467fdbfa92a712eae46dd066aa19780681a2ce266e88fbc7165"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9301cf1d7fc1ddf668d0abbe3e227fc9ab15bc036a31c247276012abb921b5ff"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e23a8c3b6c06de40bdb9e06288180d630b562db8ac199e8cc535af81f90e64b"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4a82faae00d1eed4809c2f18b37f15ce39a10a1c58fe48b60ad02875d6e13d80"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c8398a1b1951aaa330269c35335ae69744be166e67e0ebd9869bdc09426f3871"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d40682ac60b2a613d36d8d3a0cd14fbdf8e7e0618fbb40aa9fa7b796c9081584"}, - {file = "pyzmq-25.1.0-cp38-cp38-win32.whl", hash = "sha256:33d5c8391a34d56224bccf74f458d82fc6e24b3213fc68165c98b708c7a69325"}, - {file = "pyzmq-25.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c66b7ff2527e18554030319b1376d81560ca0742c6e0b17ff1ee96624a5f1afd"}, - {file = "pyzmq-25.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:af56229ea6527a849ac9fb154a059d7e32e77a8cba27e3e62a1e38d8808cb1a5"}, - {file = "pyzmq-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdca18b94c404af6ae5533cd1bc310c4931f7ac97c148bbfd2cd4bdd62b96253"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b6b42f7055bbc562f63f3df3b63e3dd1ebe9727ff0f124c3aa7bcea7b3a00f9"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c2fc7aad520a97d64ffc98190fce6b64152bde57a10c704b337082679e74f67"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be86a26415a8b6af02cd8d782e3a9ae3872140a057f1cadf0133de685185c02b"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851fb2fe14036cfc1960d806628b80276af5424db09fe5c91c726890c8e6d943"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2a21fec5c3cea45421a19ccbe6250c82f97af4175bc09de4d6dd78fb0cb4c200"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bad172aba822444b32eae54c2d5ab18cd7dee9814fd5c7ed026603b8cae2d05f"}, - {file = "pyzmq-25.1.0-cp39-cp39-win32.whl", hash = "sha256:4d67609b37204acad3d566bb7391e0ecc25ef8bae22ff72ebe2ad7ffb7847158"}, - {file = "pyzmq-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:71c7b5896e40720d30cd77a81e62b433b981005bbff0cb2f739e0f8d059b5d99"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cb27ef9d3bdc0c195b2dc54fcb8720e18b741624686a81942e14c8b67cc61a6"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c4fc2741e0513b5d5a12fe200d6785bbcc621f6f2278893a9ca7bed7f2efb7d"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fc34fdd458ff77a2a00e3c86f899911f6f269d393ca5675842a6e92eea565bae"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8751f9c1442624da391bbd92bd4b072def6d7702a9390e4479f45c182392ff78"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:6581e886aec3135964a302a0f5eb68f964869b9efd1dbafdebceaaf2934f8a68"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5482f08d2c3c42b920e8771ae8932fbaa0a67dff925fc476996ddd8155a170f3"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7fbcafa3ea16d1de1f213c226005fea21ee16ed56134b75b2dede5a2129e62"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:adecf6d02b1beab8d7c04bc36f22bb0e4c65a35eb0b4750b91693631d4081c70"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6d39e42a0aa888122d1beb8ec0d4ddfb6c6b45aecb5ba4013c27e2f28657765"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7018289b402ebf2b2c06992813523de61d4ce17bd514c4339d8f27a6f6809492"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9e68ae9864d260b18f311b68d29134d8776d82e7f5d75ce898b40a88df9db30f"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e21cc00e4debe8f54c3ed7b9fcca540f46eee12762a9fa56feb8512fd9057161"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f666ae327a6899ff560d741681fdcdf4506f990595201ed39b44278c471ad98"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f5efcc29056dfe95e9c9db0dfbb12b62db9c4ad302f812931b6d21dd04a9119"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:48e5e59e77c1a83162ab3c163fc01cd2eebc5b34560341a67421b09be0891287"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:108c96ebbd573d929740d66e4c3d1bdf31d5cde003b8dc7811a3c8c5b0fc173b"}, - {file = "pyzmq-25.1.0.tar.gz", hash = "sha256:80c41023465d36280e801564a69cbfce8ae85ff79b080e1913f6e90481fb8957"}, + {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76"}, + {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9"}, + {file = "pyzmq-25.1.1-cp310-cp310-win32.whl", hash = "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790"}, + {file = "pyzmq-25.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f"}, + {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83"}, + {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca"}, + {file = "pyzmq-25.1.1-cp311-cp311-win32.whl", hash = "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329"}, + {file = "pyzmq-25.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6"}, + {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762"}, + {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb"}, + {file = "pyzmq-25.1.1-cp312-cp312-win32.whl", hash = "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075"}, + {file = "pyzmq-25.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787"}, + {file = "pyzmq-25.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123"}, + {file = "pyzmq-25.1.1-cp36-cp36m-win32.whl", hash = "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71"}, + {file = "pyzmq-25.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3"}, + {file = "pyzmq-25.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae"}, + {file = "pyzmq-25.1.1-cp37-cp37m-win32.whl", hash = "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e"}, + {file = "pyzmq-25.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3"}, + {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a"}, + {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0"}, + {file = "pyzmq-25.1.1-cp38-cp38-win32.whl", hash = "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c"}, + {file = "pyzmq-25.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1"}, + {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45"}, + {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2"}, + {file = "pyzmq-25.1.1-cp39-cp39-win32.whl", hash = "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2"}, + {file = "pyzmq-25.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304"}, + {file = "pyzmq-25.1.1.tar.gz", hash = "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23"}, ] [package.dependencies] @@ -5516,101 +5752,119 @@ python-dateutil = ">=2.8.1,<3.0.0" typing-extensions = ">=4.2.0,<5.0.0" websockets = ">=10.3,<11.0" +[[package]] +name = "redis" +version = "4.6.0" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"}, + {file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + [[package]] name = "regex" -version = "2023.6.3" +version = "2023.8.8" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.6" files = [ - {file = "regex-2023.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:824bf3ac11001849aec3fa1d69abcb67aac3e150a933963fb12bda5151fe1bfd"}, - {file = "regex-2023.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05ed27acdf4465c95826962528f9e8d41dbf9b1aa8531a387dee6ed215a3e9ef"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b49c764f88a79160fa64f9a7b425620e87c9f46095ef9c9920542ab2495c8bc"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e3f1316c2293e5469f8f09dc2d76efb6c3982d3da91ba95061a7e69489a14ef"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43e1dd9d12df9004246bacb79a0e5886b3b6071b32e41f83b0acbf293f820ee8"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4959e8bcbfda5146477d21c3a8ad81b185cd252f3d0d6e4724a5ef11c012fb06"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af4dd387354dc83a3bff67127a124c21116feb0d2ef536805c454721c5d7993d"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2239d95d8e243658b8dbb36b12bd10c33ad6e6933a54d36ff053713f129aa536"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:890e5a11c97cf0d0c550eb661b937a1e45431ffa79803b942a057c4fb12a2da2"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a8105e9af3b029f243ab11ad47c19b566482c150c754e4c717900a798806b222"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:25be746a8ec7bc7b082783216de8e9473803706723b3f6bef34b3d0ed03d57e2"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3676f1dd082be28b1266c93f618ee07741b704ab7b68501a173ce7d8d0d0ca18"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:10cb847aeb1728412c666ab2e2000ba6f174f25b2bdc7292e7dd71b16db07568"}, - {file = "regex-2023.6.3-cp310-cp310-win32.whl", hash = "sha256:dbbbfce33cd98f97f6bffb17801b0576e653f4fdb1d399b2ea89638bc8d08ae1"}, - {file = "regex-2023.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:c5f8037000eb21e4823aa485149f2299eb589f8d1fe4b448036d230c3f4e68e0"}, - {file = "regex-2023.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c123f662be8ec5ab4ea72ea300359023a5d1df095b7ead76fedcd8babbedf969"}, - {file = "regex-2023.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9edcbad1f8a407e450fbac88d89e04e0b99a08473f666a3f3de0fd292badb6aa"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcba6dae7de533c876255317c11f3abe4907ba7d9aa15d13e3d9710d4315ec0e"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29cdd471ebf9e0f2fb3cac165efedc3c58db841d83a518b082077e612d3ee5df"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12b74fbbf6cbbf9dbce20eb9b5879469e97aeeaa874145517563cca4029db65c"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c29ca1bd61b16b67be247be87390ef1d1ef702800f91fbd1991f5c4421ebae8"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77f09bc4b55d4bf7cc5eba785d87001d6757b7c9eec237fe2af57aba1a071d9"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ea353ecb6ab5f7e7d2f4372b1e779796ebd7b37352d290096978fea83c4dba0c"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:10590510780b7541969287512d1b43f19f965c2ece6c9b1c00fc367b29d8dce7"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e2fbd6236aae3b7f9d514312cdb58e6494ee1c76a9948adde6eba33eb1c4264f"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:6b2675068c8b56f6bfd5a2bda55b8accbb96c02fd563704732fd1c95e2083461"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74419d2b50ecb98360cfaa2974da8689cb3b45b9deff0dcf489c0d333bcc1477"}, - {file = "regex-2023.6.3-cp311-cp311-win32.whl", hash = "sha256:fb5ec16523dc573a4b277663a2b5a364e2099902d3944c9419a40ebd56a118f9"}, - {file = "regex-2023.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:09e4a1a6acc39294a36b7338819b10baceb227f7f7dbbea0506d419b5a1dd8af"}, - {file = "regex-2023.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0654bca0cdf28a5956c83839162692725159f4cda8d63e0911a2c0dc76166525"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:463b6a3ceb5ca952e66550a4532cef94c9a0c80dc156c4cc343041951aec1697"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87b2a5bb5e78ee0ad1de71c664d6eb536dc3947a46a69182a90f4410f5e3f7dd"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6343c6928282c1f6a9db41f5fd551662310e8774c0e5ebccb767002fcf663ca9"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6192d5af2ccd2a38877bfef086d35e6659566a335b1492786ff254c168b1693"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74390d18c75054947e4194019077e243c06fbb62e541d8817a0fa822ea310c14"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:742e19a90d9bb2f4a6cf2862b8b06dea5e09b96c9f2df1779e53432d7275331f"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8abbc5d54ea0ee80e37fef009e3cec5dafd722ed3c829126253d3e22f3846f1e"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c2b867c17a7a7ae44c43ebbeb1b5ff406b3e8d5b3e14662683e5e66e6cc868d3"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d831c2f8ff278179705ca59f7e8524069c1a989e716a1874d6d1aab6119d91d1"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ee2d1a9a253b1729bb2de27d41f696ae893507c7db224436abe83ee25356f5c1"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:61474f0b41fe1a80e8dfa70f70ea1e047387b7cd01c85ec88fa44f5d7561d787"}, - {file = "regex-2023.6.3-cp36-cp36m-win32.whl", hash = "sha256:0b71e63226e393b534105fcbdd8740410dc6b0854c2bfa39bbda6b0d40e59a54"}, - {file = "regex-2023.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bbb02fd4462f37060122e5acacec78e49c0fbb303c30dd49c7f493cf21fc5b27"}, - {file = "regex-2023.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b862c2b9d5ae38a68b92e215b93f98d4c5e9454fa36aae4450f61dd33ff48487"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976d7a304b59ede34ca2921305b57356694f9e6879db323fd90a80f865d355a3"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:83320a09188e0e6c39088355d423aa9d056ad57a0b6c6381b300ec1a04ec3d16"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9427a399501818a7564f8c90eced1e9e20709ece36be701f394ada99890ea4b3"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178bbc1b2ec40eaca599d13c092079bf529679bf0371c602edaa555e10b41c3"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:837328d14cde912af625d5f303ec29f7e28cdab588674897baafaf505341f2fc"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d44dc13229905ae96dd2ae2dd7cebf824ee92bc52e8cf03dcead37d926da019"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d54af539295392611e7efbe94e827311eb8b29668e2b3f4cadcfe6f46df9c777"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7117d10690c38a622e54c432dfbbd3cbd92f09401d622902c32f6d377e2300ee"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bb60b503ec8a6e4e3e03a681072fa3a5adcbfa5479fa2d898ae2b4a8e24c4591"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:65ba8603753cec91c71de423a943ba506363b0e5c3fdb913ef8f9caa14b2c7e0"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:271f0bdba3c70b58e6f500b205d10a36fb4b58bd06ac61381b68de66442efddb"}, - {file = "regex-2023.6.3-cp37-cp37m-win32.whl", hash = "sha256:9beb322958aaca059f34975b0df135181f2e5d7a13b84d3e0e45434749cb20f7"}, - {file = "regex-2023.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fea75c3710d4f31389eed3c02f62d0b66a9da282521075061ce875eb5300cf23"}, - {file = "regex-2023.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f56fcb7ff7bf7404becdfc60b1e81a6d0561807051fd2f1860b0d0348156a07"}, - {file = "regex-2023.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d2da3abc88711bce7557412310dfa50327d5769a31d1c894b58eb256459dc289"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a99b50300df5add73d307cf66abea093304a07eb017bce94f01e795090dea87c"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5708089ed5b40a7b2dc561e0c8baa9535b77771b64a8330b684823cfd5116036"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:687ea9d78a4b1cf82f8479cab23678aff723108df3edeac098e5b2498879f4a7"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3850beab9f527f06ccc94b446c864059c57651b3f911fddb8d9d3ec1d1b25d"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8915cc96abeb8983cea1df3c939e3c6e1ac778340c17732eb63bb96247b91d2"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:841d6e0e5663d4c7b4c8099c9997be748677d46cbf43f9f471150e560791f7ff"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9edce5281f965cf135e19840f4d93d55b3835122aa76ccacfd389e880ba4cf82"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b956231ebdc45f5b7a2e1f90f66a12be9610ce775fe1b1d50414aac1e9206c06"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:36efeba71c6539d23c4643be88295ce8c82c88bbd7c65e8a24081d2ca123da3f"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:cf67ca618b4fd34aee78740bea954d7c69fdda419eb208c2c0c7060bb822d747"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b4598b1897837067a57b08147a68ac026c1e73b31ef6e36deeeb1fa60b2933c9"}, - {file = "regex-2023.6.3-cp38-cp38-win32.whl", hash = "sha256:f415f802fbcafed5dcc694c13b1292f07fe0befdb94aa8a52905bd115ff41e88"}, - {file = "regex-2023.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:d4f03bb71d482f979bda92e1427f3ec9b220e62a7dd337af0aa6b47bf4498f72"}, - {file = "regex-2023.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccf91346b7bd20c790310c4147eee6ed495a54ddb6737162a36ce9dbef3e4751"}, - {file = "regex-2023.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b28f5024a3a041009eb4c333863d7894d191215b39576535c6734cd88b0fcb68"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0bb18053dfcfed432cc3ac632b5e5e5c5b7e55fb3f8090e867bfd9b054dbcbf"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5bfb3004f2144a084a16ce19ca56b8ac46e6fd0651f54269fc9e230edb5e4a"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c6b48d0fa50d8f4df3daf451be7f9689c2bde1a52b1225c5926e3f54b6a9ed1"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:051da80e6eeb6e239e394ae60704d2b566aa6a7aed6f2890a7967307267a5dc6"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4c3b7fa4cdaa69268748665a1a6ff70c014d39bb69c50fda64b396c9116cf77"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:457b6cce21bee41ac292d6753d5e94dcbc5c9e3e3a834da285b0bde7aa4a11e9"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aad51907d74fc183033ad796dd4c2e080d1adcc4fd3c0fd4fd499f30c03011cd"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0385e73da22363778ef2324950e08b689abdf0b108a7d8decb403ad7f5191938"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c6a57b742133830eec44d9b2290daf5cbe0a2f1d6acee1b3c7b1c7b2f3606df7"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3e5219bf9e75993d73ab3d25985c857c77e614525fac9ae02b1bebd92f7cecac"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e5087a3c59eef624a4591ef9eaa6e9a8d8a94c779dade95d27c0bc24650261cd"}, - {file = "regex-2023.6.3-cp39-cp39-win32.whl", hash = "sha256:20326216cc2afe69b6e98528160b225d72f85ab080cbdf0b11528cbbaba2248f"}, - {file = "regex-2023.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:bdff5eab10e59cf26bc479f565e25ed71a7d041d1ded04ccf9aee1d9f208487a"}, - {file = "regex-2023.6.3.tar.gz", hash = "sha256:72d1a25bf36d2050ceb35b517afe13864865268dfb45910e2e17a84be6cbfeb0"}, + {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"}, + {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"}, + {file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"}, + {file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"}, + {file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"}, + {file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"}, + {file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"}, + {file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"}, + {file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"}, + {file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"}, + {file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"}, + {file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"}, + {file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"}, + {file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"}, + {file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"}, + {file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"}, + {file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"}, + {file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"}, + {file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"}, + {file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"}, + {file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"}, + {file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"}, + {file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"}, ] [[package]] @@ -5725,59 +5979,49 @@ files = [ [[package]] name = "safetensors" -version = "0.3.1" +version = "0.3.2" description = "Fast and Safe Tensor serialization" optional = true python-versions = "*" files = [ - {file = "safetensors-0.3.1-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:2ae9b7dd268b4bae6624729dac86deb82104820e9786429b0583e5168db2f770"}, - {file = "safetensors-0.3.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:08c85c1934682f1e2cd904d38433b53cd2a98245a7cc31f5689f9322a2320bbf"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba625c7af9e1c5d0d91cb83d2fba97d29ea69d4db2015d9714d24c7f6d488e15"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b57d5890c619ec10d9f1b6426b8690d0c9c2868a90dc52f13fae6f6407ac141f"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c9f562ea696d50b95cadbeb1716dc476714a87792ffe374280c0835312cbfe2"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c115951b3a865ece8d98ee43882f2fd0a999c0200d6e6fec24134715ebe3b57"}, - {file = "safetensors-0.3.1-cp310-cp310-win32.whl", hash = "sha256:118f8f7503ea312fc7af27e934088a1b589fb1eff5a7dea2cd1de6c71ee33391"}, - {file = "safetensors-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:54846eaae25fded28a7bebbb66be563cad221b4c80daee39e2f55df5e5e0266f"}, - {file = "safetensors-0.3.1-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:5af82e10946c4822506db0f29269f43147e889054704dde994d4e22f0c37377b"}, - {file = "safetensors-0.3.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:626c86dd1d930963c8ea7f953a3787ae85322551e3a5203ac731d6e6f3e18f44"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12e30677e6af1f4cc4f2832546e91dbb3b0aa7d575bfa473d2899d524e1ace08"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d534b80bc8d39945bb902f34b0454773971fe9e5e1f2142af451759d7e52b356"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ddd0ddd502cf219666e7d30f23f196cb87e829439b52b39f3e7da7918c3416df"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997a2cc14023713f423e6d16536d55cb16a3d72850f142e05f82f0d4c76d383b"}, - {file = "safetensors-0.3.1-cp311-cp311-win32.whl", hash = "sha256:6ae9ca63d9e22f71ec40550207bd284a60a6b4916ae6ca12c85a8d86bf49e0c3"}, - {file = "safetensors-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:62aa7421ca455418423e35029524489480adda53e3f702453580180ecfebe476"}, - {file = "safetensors-0.3.1-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:6d54b3ed367b6898baab75dfd057c24f36ec64d3938ffff2af981d56bfba2f42"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:262423aeda91117010f8c607889066028f680fbb667f50cfe6eae96f22f9d150"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10efe2513a8327fd628cea13167089588acc23093ba132aecfc536eb9a4560fe"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:689b3d6a7ebce70ee9438267ee55ea89b575c19923876645e927d08757b552fe"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14cd9a87bc73ce06903e9f8ee8b05b056af6f3c9f37a6bd74997a16ed36ff5f4"}, - {file = "safetensors-0.3.1-cp37-cp37m-win32.whl", hash = "sha256:a77cb39624480d5f143c1cc272184f65a296f573d61629eff5d495d2e0541d3e"}, - {file = "safetensors-0.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9eff3190bfbbb52eef729911345c643f875ca4dbb374aa6c559675cfd0ab73db"}, - {file = "safetensors-0.3.1-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:05cbfef76e4daa14796db1bbb52072d4b72a44050c368b2b1f6fd3e610669a89"}, - {file = "safetensors-0.3.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:c49061461f4a81e5ec3415070a3f135530834c89cbd6a7db7cd49e3cb9d9864b"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cf7e73ca42974f098ce0cf4dd8918983700b6b07a4c6827d50c8daefca776e"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04f909442d6223ff0016cd2e1b2a95ef8039b92a558014627363a2e267213f62"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c573c5a0d5d45791ae8c179e26d74aff86e719056591aa7edb3ca7be55bc961"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6994043b12e717cf2a6ba69077ac41f0d3675b2819734f07f61819e854c622c7"}, - {file = "safetensors-0.3.1-cp38-cp38-win32.whl", hash = "sha256:158ede81694180a0dbba59422bc304a78c054b305df993c0c6e39c6330fa9348"}, - {file = "safetensors-0.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:afdc725beff7121ea8d39a7339f5a6abcb01daa189ea56290b67fe262d56e20f"}, - {file = "safetensors-0.3.1-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:cba910fcc9e5e64d32d62b837388721165e9c7e45d23bc3a38ad57694b77f40d"}, - {file = "safetensors-0.3.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a4f7dbfe7285573cdaddd85ef6fa84ebbed995d3703ab72d71257944e384612f"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54aed0802f9eaa83ca7b1cbb986bfb90b8e2c67b6a4bcfe245627e17dad565d4"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34b75a766f3cfc99fd4c33e329b76deae63f5f388e455d863a5d6e99472fca8e"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a0f31904f35dc14919a145b2d7a2d8842a43a18a629affe678233c4ea90b4af"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcf527ecc5f58907fd9031510378105487f318cc91ecdc5aee3c7cc8f46030a8"}, - {file = "safetensors-0.3.1-cp39-cp39-win32.whl", hash = "sha256:e2f083112cf97aa9611e2a05cc170a2795eccec5f6ff837f4565f950670a9d83"}, - {file = "safetensors-0.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:5f4f614b8e8161cd8a9ca19c765d176a82b122fa3d3387b77862145bfe9b4e93"}, - {file = "safetensors-0.3.1.tar.gz", hash = "sha256:571da56ff8d0bec8ae54923b621cda98d36dcef10feb36fd492c4d0c2cd0e869"}, + {file = "safetensors-0.3.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b6a66989075c2891d743153e8ba9ca84ee7232c8539704488f454199b8b8f84d"}, + {file = "safetensors-0.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:670d6bc3a3b377278ce2971fa7c36ebc0a35041c4ea23b9df750a39380800195"}, + {file = "safetensors-0.3.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:7f80af7e4ab3188daaff12d43d078da3017a90d732d38d7af4eb08b6ca2198a5"}, + {file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb44e140bf2aeda98d9dde669dbec15f7b77f96a9274469b91a6cf4bcc5ec3b"}, + {file = "safetensors-0.3.2-cp310-cp310-win32.whl", hash = "sha256:2961c1243fd0da46aa6a1c835305cc4595486f8ac64632a604d0eb5f2de76175"}, + {file = "safetensors-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c813920482c337d1424d306e1b05824a38e3ef94303748a0a287dea7a8c4f805"}, + {file = "safetensors-0.3.2-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:707df34bd9b9047e97332136ad98e57028faeccdb9cfe1c3b52aba5964cc24bf"}, + {file = "safetensors-0.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:becc5bb85b2947eae20ed23b407ebfd5277d9a560f90381fe2c42e6c043677ba"}, + {file = "safetensors-0.3.2-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:54ad6af663e15e2b99e2ea3280981b7514485df72ba6d014dc22dae7ba6a5e6c"}, + {file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0fac127ff8fb04834da5c6d85a8077e6a1c9180a11251d96f8068db922a17"}, + {file = "safetensors-0.3.2-cp311-cp311-win32.whl", hash = "sha256:155b82dbe2b0ebff18cde3f76b42b6d9470296e92561ef1a282004d449fa2b4c"}, + {file = "safetensors-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:a86428d196959619ce90197731be9391b5098b35100a7228ef4643957648f7f5"}, + {file = "safetensors-0.3.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:c1f8ab41ed735c5b581f451fd15d9602ff51aa88044bfa933c5fa4b1d0c644d1"}, + {file = "safetensors-0.3.2-cp37-cp37m-macosx_13_0_x86_64.whl", hash = "sha256:bc9cfb3c9ea2aec89685b4d656f9f2296f0f0d67ecf2bebf950870e3be89b3db"}, + {file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d7d70d48585fe8df00725aa788f2e64fd24a4c9ae07cd6be34f6859d0f89a9c"}, + {file = "safetensors-0.3.2-cp37-cp37m-win32.whl", hash = "sha256:6ff59bc90cdc857f68b1023be9085fda6202bbe7f2fd67d06af8f976d6adcc10"}, + {file = "safetensors-0.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8b05c93da15fa911763a89281906ca333ed800ab0ef1c7ce53317aa1a2322f19"}, + {file = "safetensors-0.3.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:8969cfd9e8d904e8d3c67c989e1bd9a95e3cc8980d4f95e4dcd43c299bb94253"}, + {file = "safetensors-0.3.2-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:f54148ac027556eb02187e9bc1556c4d916c99ca3cb34ca36a7d304d675035c1"}, + {file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa98f49e95f02eb750d32c4947e7d5aa43883149ebd0414920866446525b70f0"}, + {file = "safetensors-0.3.2-cp38-cp38-win32.whl", hash = "sha256:33409df5e28a83dc5cc5547a3ac17c0f1b13a1847b1eb3bc4b3be0df9915171e"}, + {file = "safetensors-0.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:e04a7cbbb3856159ab99e3adb14521544f65fcb8548cce773a1435a0f8d78d27"}, + {file = "safetensors-0.3.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:7c864cf5dcbfb608c5378f83319c60cc9c97263343b57c02756b7613cd5ab4dd"}, + {file = "safetensors-0.3.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:14e8c19d6dc51d4f70ee33c46aff04c8ba3f95812e74daf8036c24bc86e75cae"}, + {file = "safetensors-0.3.2-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:fafd95e5ef41e8f312e2a32b7031f7b9b2a621b255f867b221f94bb2e9f51ae8"}, + {file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87ff0024ef2e5722a79af24688ce4a430f70601d0cf712a744105ed4b8f67ba5"}, + {file = "safetensors-0.3.2-cp39-cp39-win32.whl", hash = "sha256:827af9478b78977248ba93e2fd97ea307fb63f463f80cef4824460f8c2542a52"}, + {file = "safetensors-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9b09f27c456efa301f98681ea14b12f81f2637889f6336223ccab71e42c34541"}, + {file = "safetensors-0.3.2.tar.gz", hash = "sha256:2dbd34554ed3b99435a0e84df077108f5334c8336b5ed9cb8b6b98f7b10da2f6"}, ] [package.extras] -all = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (>=2.11.0)", "torch (>=1.10)"] -dev = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (>=2.11.0)", "torch (>=1.10)"] +all = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (==2.11.0)", "torch (>=1.10)"] +dev = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (==2.11.0)", "torch (>=1.10)"] jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)"] numpy = ["numpy (>=1.21.6)"] paddlepaddle = ["paddlepaddle (>=2.4.1)"] +pinned-tf = ["tensorflow (==2.11.0)"] quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] tensorflow = ["tensorflow (>=2.11.0)"] testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "numpy (>=1.21.6)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)"] @@ -6603,40 +6847,40 @@ scipy = ["scipy"] [[package]] name = "tornado" -version = "6.3.2" +version = "6.3.3" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">= 3.8" files = [ - {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"}, - {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"}, - {file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"}, - {file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"}, - {file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"}, + {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d"}, + {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17"}, + {file = "tornado-6.3.3-cp38-abi3-win32.whl", hash = "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3"}, + {file = "tornado-6.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5"}, + {file = "tornado-6.3.3.tar.gz", hash = "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe"}, ] [[package]] name = "tqdm" -version = "4.65.0" +version = "4.66.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, - {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -7045,19 +7289,25 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my [[package]] name = "validators" -version = "0.20.0" -description = "Python Data Validation for Humans™." +version = "0.21.0" +description = "Python Data Validation for Humans™" optional = false -python-versions = ">=3.4" +python-versions = ">=3.8,<4.0" files = [ - {file = "validators-0.20.0.tar.gz", hash = "sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a"}, + {file = "validators-0.21.0-py3-none-any.whl", hash = "sha256:3470db6f2384c49727ee319afa2e97aec3f8fad736faa6067e0fd7f9eaf2c551"}, + {file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"}, ] -[package.dependencies] -decorator = ">=3.4.0" - -[package.extras] -test = ["flake8 (>=2.4.0)", "isort (>=4.2.2)", "pytest (>=2.2.3)"] +[[package]] +name = "vine" +version = "5.0.0" +description = "Promises, promises, promises." +optional = false +python-versions = ">=3.6" +files = [ + {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, + {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, +] [[package]] name = "watchfiles" @@ -7550,4 +7800,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "1329d94d3cb37062393d79da99fb3fa7d214ebdcdab6402c411561f960c6689f" +content-hash = "470ce5cd8ad64d3f6d004cebdb5625c86bd083dc8e4683e084d531e1ccbf90c9" diff --git a/pyproject.toml b/pyproject.toml index 019632e46..03641d7cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ python-multipart = "^0.0.6" sqlmodel = "^0.0.8" faiss-cpu = "^1.7.4" anthropic = "^0.3.0" -orjson = "^3.9.1" +orjson = "3.9.3" multiprocess = "^0.70.14" cachetools = "^5.3.1" types-cachetools = "^5.3.0.5" @@ -77,6 +77,9 @@ psycopg = "^3.1.9" psycopg-binary = "^3.1.9" fastavro = "^1.8.0" langchain-experimental = "^0.0.8" +celery = "^5.3.1" +redis = "^4.6.0" +flower = "^2.0.1" [tool.poetry.group.dev.dependencies] black = "^23.1.0" From 43ba81c2ee45c76e9d93b253031e7a06d262f5b9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 19:25:28 -0300 Subject: [PATCH 006/213] =?UTF-8?q?=F0=9F=93=A6=20chore(core):=20add=20Cel?= =?UTF-8?q?ery=20configuration=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ feat(celery_app.py): add Celery app configuration for langflow with Redis as broker and backend ✨ feat(celeryconfig.py): add Celery configuration file with environment variable support for broker URL and result backend URL --- src/backend/langflow/core/__init__.py | 0 src/backend/langflow/core/celery_app.py | 7 +++++++ src/backend/langflow/core/celeryconfig.py | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 src/backend/langflow/core/__init__.py create mode 100644 src/backend/langflow/core/celery_app.py create mode 100644 src/backend/langflow/core/celeryconfig.py diff --git a/src/backend/langflow/core/__init__.py b/src/backend/langflow/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/core/celery_app.py b/src/backend/langflow/core/celery_app.py new file mode 100644 index 000000000..7b89a05ab --- /dev/null +++ b/src/backend/langflow/core/celery_app.py @@ -0,0 +1,7 @@ +from celery import Celery + +celery_app = Celery( + "langflow", broker="redis://queue:6379/0", backend="redis://queue:6379/0" +) +# command: celery -A langflow.worker.celery_app worker --loglevel=INFO +celery_app.conf.task_routes = {"langflow.worker.tasks.*": {"queue": "langflow"}} diff --git a/src/backend/langflow/core/celeryconfig.py b/src/backend/langflow/core/celeryconfig.py new file mode 100644 index 000000000..647a7f93d --- /dev/null +++ b/src/backend/langflow/core/celeryconfig.py @@ -0,0 +1,7 @@ +# celeryconfig.py +import os + +broker_url = os.environ.get("BROKER_URL", "redis://localhost:6379/0") +result_backend = os.environ.get("RESULT_BACKEND", "redis://localhost:6379/0") +# tasks should be json or pickle +accept_content = ["json", "pickle"] From 304e824ef781e8b847afa519c4efbf4d888fcb27 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 19:25:59 -0300 Subject: [PATCH 007/213] =?UTF-8?q?=E2=9C=A8=20feat(worker.py):=20add=20wo?= =?UTF-8?q?rker=20module=20to=20handle=20background=20tasks=20in=20LangFlo?= =?UTF-8?q?w=20backend=20=F0=9F=94=A7=20chore(worker.py):=20refactor=20pro?= =?UTF-8?q?cess=5Fgraph=5Fcached=20function=20to=20improve=20readability?= =?UTF-8?q?=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/worker.py | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/backend/langflow/worker.py diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py new file mode 100644 index 000000000..d390705f1 --- /dev/null +++ b/src/backend/langflow/worker.py @@ -0,0 +1,62 @@ +from langflow.core.celery_app import celery_app +from typing import Any, Dict, Optional + + +@celery_app.task(acks_late=True) +def test_celery(word: str) -> str: + return f"test task return {word}" + + +@celery_app.task(acks_late=True) +def process_graph_cached( + data_graph: Dict[str, Any], inputs: Optional[dict] = None, clear_cache=False +): + """ + Process graph by extracting input variables and replacing ZeroShotPrompt + with PromptTemplate,then run the graph and return the result and thought. + """ + from langflow.interface.run import build_sorted_vertices_with_caching + from langflow.processing.process import get_result_and_thought + from langchain.chains.base import Chain + from langchain.vectorstores.base import VectorStore + from langflow.utils.logger import logger + + # Load langchain object + if clear_cache: + build_sorted_vertices_with_caching.clear_cache() + logger.debug("Cleared cache") + langchain_object, artifacts = build_sorted_vertices_with_caching(data_graph) + logger.debug("Loaded LangChain object") + if inputs is None: + inputs = {} + + # Add artifacts to inputs + # artifacts can be documents loaded when building + # the flow + for ( + key, + value, + ) in artifacts.items(): + if key not in inputs or not inputs[key]: + inputs[key] = value + + if langchain_object is None: + # Raise user facing error + raise ValueError( + "There was an error loading the langchain_object. Please, check all the nodes and try again." + ) + + # Generate result and thought + if isinstance(langchain_object, Chain): + if inputs is None: + raise ValueError("Inputs must be provided for a Chain") + logger.debug("Generating result and thought") + result = get_result_and_thought(langchain_object, inputs) + logger.debug("Generated result and thought") + elif isinstance(langchain_object, VectorStore): + result = langchain_object.search(**inputs) + else: + raise ValueError( + f"Unknown langchain_object type: {type(langchain_object).__name__}" + ) + return result From 5954e6daa0c097f125a62f73b841fdf57c2ce564 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 19:26:48 -0300 Subject: [PATCH 008/213] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20rem?= =?UTF-8?q?ove=20unused=20import=20'process=5Fgraph=5Fcached'=20and=20repl?= =?UTF-8?q?ace=20it=20with=20'process=5Fgraph=5Fcached=5Fworker'=20for=20p?= =?UTF-8?q?rocessing=20graph=20data=20asynchronously=20=F0=9F=94=A7=20fix(?= =?UTF-8?q?endpoints.py):=20add=20error=20handling=20for=20processing=20tw?= =?UTF-8?q?eaks=20and=20log=20the=20error=20message=20=E2=9C=A8=20feat(end?= =?UTF-8?q?points.py):=20use=20'process=5Fgraph=5Fcached=5Fworker'=20to=20?= =?UTF-8?q?process=20graph=20data=20asynchronously=20and=20return=20the=20?= =?UTF-8?q?result=20in=20'ProcessResponse'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 24af55588..34f81cecf 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -3,10 +3,10 @@ from typing import Annotated, Optional from langflow.cache.utils import save_uploaded_file from langflow.database.models.flow import Flow -from langflow.processing.process import process_graph_cached, process_tweaks +from langflow.processing.process import process_tweaks from langflow.utils.logger import logger from langflow.settings import settings - +from langflow.worker import process_graph_cached as process_graph_cached_worker from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body from langflow.interface.custom.custom_component import CustomComponent @@ -90,7 +90,12 @@ async def process_flow( graph_data = process_tweaks(graph_data, tweaks) except Exception as exc: logger.error(f"Error processing tweaks: {exc}") - response = process_graph_cached(graph_data, inputs, clear_cache) + # ! This was added just for testing purposes + response = process_graph_cached_worker.delay( + graph_data=graph_data, + inputs=inputs, + clear_cache=clear_cache, + ).get() return ProcessResponse( result=response, ) From 34ac959f5a4b8d50c7c58af9b9042c56c69cb1e7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:37:22 -0300 Subject: [PATCH 009/213] =?UTF-8?q?=F0=9F=94=84=20refactor(cache):=20reorg?= =?UTF-8?q?anize=20imports=20and=20remove=20unused=20imports=20and=20varia?= =?UTF-8?q?bles=20in=20cache=20module=20=F0=9F=94=84=20refactor(cache):=20?= =?UTF-8?q?rename=20BaseCache=20class=20to=20BaseCacheManager=20for=20bett?= =?UTF-8?q?er=20clarity=20and=20consistency=20=F0=9F=94=84=20refactor(cach?= =?UTF-8?q?e):=20remove=20cache=5Fmanager=20from=20=5F=5Fall=5F=5F=20in=20?= =?UTF-8?q?cache=20module=20=F0=9F=94=84=20refactor(cache):=20remove=20unu?= =?UTF-8?q?sed=20import=20of=20Service=20in=20BaseCacheManager=20class=20?= =?UTF-8?q?=F0=9F=94=84=20refactor(cache):=20rename=20cache=5Fmanager=20to?= =?UTF-8?q?=20BaseCacheManager=20in=20factory=20module=20=F0=9F=94=84=20re?= =?UTF-8?q?factor(cache):=20remove=20unused=20import=20of=20InMemoryCache?= =?UTF-8?q?=20in=20factory=20module=20=F0=9F=94=84=20refactor(cache):=20re?= =?UTF-8?q?move=20flow=20module=20and=20its=20related=20code=20as=20it=20i?= =?UTF-8?q?s=20no=20longer=20used?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 fix(manager.py): fix import statements and class inheritance in cache manager module ✨ feat(manager.py): add InMemoryCache class to implement a simple in-memory cache using an OrderedDict ✨ feat(manager.py): add RedisCache class to implement a Redis-based cache using the redis-py library --- .../langflow/services/cache/__init__.py | 4 +- src/backend/langflow/services/cache/base.py | 6 +- .../langflow/services/cache/factory.py | 26 +- src/backend/langflow/services/cache/flow.py | 146 -------- .../langflow/services/cache/manager.py | 351 +++++++++++------- 5 files changed, 256 insertions(+), 277 deletions(-) delete mode 100644 src/backend/langflow/services/cache/flow.py diff --git a/src/backend/langflow/services/cache/__init__.py b/src/backend/langflow/services/cache/__init__.py index 79e143807..3b122aa9e 100644 --- a/src/backend/langflow/services/cache/__init__.py +++ b/src/backend/langflow/services/cache/__init__.py @@ -1,10 +1,8 @@ from . import factory, manager -from langflow.services.cache.manager import cache_manager -from langflow.services.cache.flow import InMemoryCache +from langflow.services.cache.manager import InMemoryCache __all__ = [ - "cache_manager", "factory", "manager", "InMemoryCache", diff --git a/src/backend/langflow/services/cache/base.py b/src/backend/langflow/services/cache/base.py index 88cb3a1da..80aec1408 100644 --- a/src/backend/langflow/services/cache/base.py +++ b/src/backend/langflow/services/cache/base.py @@ -1,11 +1,15 @@ import abc +from langflow.services.base import Service -class BaseCache(abc.ABC): + +class BaseCacheManager(abc.ABC, Service): """ Abstract base class for a cache. """ + name = "cache_manager" + @abc.abstractmethod def get(self, key): """ diff --git a/src/backend/langflow/services/cache/factory.py b/src/backend/langflow/services/cache/factory.py index 77f8d58d1..78e307bfa 100644 --- a/src/backend/langflow/services/cache/factory.py +++ b/src/backend/langflow/services/cache/factory.py @@ -1,11 +1,31 @@ -from langflow.services.cache.manager import CacheManager +from langflow.services.cache.manager import InMemoryCache, RedisCache, BaseCacheManager from langflow.services.factory import ServiceFactory +from langflow.utils.logger import logger class CacheManagerFactory(ServiceFactory): def __init__(self): - super().__init__(CacheManager) + super().__init__(BaseCacheManager) def create(self, settings_service): # Here you would have logic to create and configure a CacheManager - return CacheManager() + # based on the settings_service + + if settings_service.settings.CACHE_TYPE == "redis": + logger.debug("Creating Redis cache") + redis_cache = RedisCache( + host=settings_service.settings.REDIS_HOST, + port=settings_service.settings.REDIS_PORT, + db=settings_service.settings.REDIS_DB, + expiration_time=settings_service.settings.REDIS_CACHE_EXPIRE, + ) + if redis_cache.is_connected(): + logger.debug("Redis cache is connected") + return redis_cache + logger.warning( + "Redis cache is not connected, falling back to in-memory cache" + ) + return InMemoryCache() + + elif settings_service.settings.CACHE_TYPE == "memory": + return InMemoryCache() diff --git a/src/backend/langflow/services/cache/flow.py b/src/backend/langflow/services/cache/flow.py deleted file mode 100644 index 0c10c51e1..000000000 --- a/src/backend/langflow/services/cache/flow.py +++ /dev/null @@ -1,146 +0,0 @@ -import threading -import time -from collections import OrderedDict - -from langflow.services.cache.base import BaseCache - - -class InMemoryCache(BaseCache): - """ - A simple in-memory cache using an OrderedDict. - - This cache supports setting a maximum size and expiration time for cached items. - When the cache is full, it uses a Least Recently Used (LRU) eviction policy. - Thread-safe using a threading Lock. - - Attributes: - max_size (int, optional): Maximum number of items to store in the cache. - expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. - - Example: - - cache = InMemoryCache(max_size=3, expiration_time=5) - - # setting cache values - cache.set("a", 1) - cache.set("b", 2) - cache["c"] = 3 - - # getting cache values - a = cache.get("a") - b = cache["b"] - """ - - def __init__(self, max_size=None, expiration_time=60 * 60): - """ - Initialize a new InMemoryCache instance. - - Args: - max_size (int, optional): Maximum number of items to store in the cache. - expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. - """ - self._cache = OrderedDict() - self._lock = threading.Lock() - self.max_size = max_size - self.expiration_time = expiration_time - - def get(self, key): - """ - Retrieve an item from the cache. - - Args: - key: The key of the item to retrieve. - - Returns: - The value associated with the key, or None if the key is not found or the item has expired. - """ - with self._lock: - if key in self._cache: - item = self._cache.pop(key) - if ( - self.expiration_time is None - or time.time() - item["time"] < self.expiration_time - ): - # Move the key to the end to make it recently used - self._cache[key] = item - return item["value"] - else: - self.delete(key) - return None - - def set(self, key, value): - """ - Add an item to the cache. - - If the cache is full, the least recently used item is evicted. - - Args: - key: The key of the item. - value: The value to cache. - """ - with self._lock: - if key in self._cache: - # Remove existing key before re-inserting to update order - self.delete(key) - elif self.max_size and len(self._cache) >= self.max_size: - # Remove least recently used item - self._cache.popitem(last=False) - self._cache[key] = {"value": value, "time": time.time()} - - def get_or_set(self, key, value): - """ - Retrieve an item from the cache. If the item does not exist, set it with the provided value. - - Args: - key: The key of the item. - value: The value to cache if the item doesn't exist. - - Returns: - The cached value associated with the key. - """ - with self._lock: - if key in self._cache: - return self.get(key) - self.set(key, value) - return value - - def delete(self, key): - """ - Remove an item from the cache. - - Args: - key: The key of the item to remove. - """ - # with self._lock: - self._cache.pop(key, None) - - def clear(self): - """ - Clear all items from the cache. - """ - with self._lock: - self._cache.clear() - - def __contains__(self, key): - """Check if the key is in the cache.""" - return key in self._cache - - def __getitem__(self, key): - """Retrieve an item from the cache using the square bracket notation.""" - return self.get(key) - - def __setitem__(self, key, value): - """Add an item to the cache using the square bracket notation.""" - self.set(key, value) - - def __delitem__(self, key): - """Remove an item from the cache using the square bracket notation.""" - self.delete(key) - - def __len__(self): - """Return the number of items in the cache.""" - return len(self._cache) - - def __repr__(self): - """Return a string representation of the InMemoryCache instance.""" - return f"InMemoryCache(max_size={self.max_size}, expiration_time={self.expiration_time})" diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index ce9a338ef..6243c2507 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -1,153 +1,256 @@ -from contextlib import contextmanager -from typing import Any, Awaitable, Callable, List, Optional -from langflow.services.base import Service +import threading +import time +from collections import OrderedDict -import pandas as pd -from PIL import Image +from langflow.services.cache.base import BaseCacheManager + +import pickle +import redis -class Subject: - """Base class for implementing the observer pattern.""" +class InMemoryCache(BaseCacheManager): - def __init__(self): - self.observers: List[Callable[[], None]] = [] + """ + A simple in-memory cache using an OrderedDict. - def attach(self, observer: Callable[[], None]): - """Attach an observer to the subject.""" - self.observers.append(observer) + This cache supports setting a maximum size and expiration time for cached items. + When the cache is full, it uses a Least Recently Used (LRU) eviction policy. + Thread-safe using a threading Lock. - def detach(self, observer: Callable[[], None]): - """Detach an observer from the subject.""" - self.observers.remove(observer) + Attributes: + max_size (int, optional): Maximum number of items to store in the cache. + expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. - def notify(self): - """Notify all observers about an event.""" - for observer in self.observers: - if observer is None: - continue - observer() + Example: + cache = InMemoryCache(max_size=3, expiration_time=5) -class AsyncSubject: - """Base class for implementing the async observer pattern.""" + # setting cache values + cache.set("a", 1) + cache.set("b", 2) + cache["c"] = 3 - def __init__(self): - self.observers: List[Callable[[], Awaitable]] = [] + # getting cache values + a = cache.get("a") + b = cache["b"] + """ - def attach(self, observer: Callable[[], Awaitable]): - """Attach an observer to the subject.""" - self.observers.append(observer) - - def detach(self, observer: Callable[[], Awaitable]): - """Detach an observer from the subject.""" - self.observers.remove(observer) - - async def notify(self): - """Notify all observers about an event.""" - for observer in self.observers: - if observer is None: - continue - await observer() - - -class CacheManager(Subject, Service): - """Manages cache for different clients and notifies observers on changes.""" - - name = "cache_manager" - - def __init__(self): - super().__init__() - self._cache = {} - self.current_client_id = None - self.current_cache = {} - - @contextmanager - def set_client_id(self, client_id: str): + def __init__(self, max_size=None, expiration_time=60 * 60): """ - Context manager to set the current client_id and associated cache. + Initialize a new InMemoryCache instance. Args: - client_id (str): The client identifier. + max_size (int, optional): Maximum number of items to store in the cache. + expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. + """ + self._cache = OrderedDict() + self._lock = threading.Lock() + self.max_size = max_size + self.expiration_time = expiration_time + + def get(self, key): + """ + Retrieve an item from the cache. + + Args: + key: The key of the item to retrieve. + + Returns: + The value associated with the key, or None if the key is not found or the item has expired. + """ + with self._lock: + if key in self._cache: + item = self._cache.pop(key) + if ( + self.expiration_time is None + or time.time() - item["time"] < self.expiration_time + ): + # Move the key to the end to make it recently used + self._cache[key] = item + return item["value"] + else: + self.delete(key) + return None + + def set(self, key, value): + """ + Add an item to the cache. + + If the cache is full, the least recently used item is evicted. + + Args: + key: The key of the item. + value: The value to cache. + """ + with self._lock: + if key in self._cache: + # Remove existing key before re-inserting to update order + self.delete(key) + elif self.max_size and len(self._cache) >= self.max_size: + # Remove least recently used item + self._cache.popitem(last=False) + self._cache[key] = {"value": value, "time": time.time()} + + def get_or_set(self, key, value): + """ + Retrieve an item from the cache. If the item does not exist, set it with the provided value. + + Args: + key: The key of the item. + value: The value to cache if the item doesn't exist. + + Returns: + The cached value associated with the key. + """ + with self._lock: + if key in self._cache: + return self.get(key) + self.set(key, value) + return value + + def delete(self, key): + """ + Remove an item from the cache. + + Args: + key: The key of the item to remove. + """ + # with self._lock: + self._cache.pop(key, None) + + def clear(self): + """ + Clear all items from the cache. + """ + with self._lock: + self._cache.clear() + + def __contains__(self, key): + """Check if the key is in the cache.""" + return key in self._cache + + def __getitem__(self, key): + """Retrieve an item from the cache using the square bracket notation.""" + return self.get(key) + + def __setitem__(self, key, value): + """Add an item to the cache using the square bracket notation.""" + self.set(key, value) + + def __delitem__(self, key): + """Remove an item from the cache using the square bracket notation.""" + self.delete(key) + + def __len__(self): + """Return the number of items in the cache.""" + return len(self._cache) + + def __repr__(self): + """Return a string representation of the InMemoryCache instance.""" + return f"InMemoryCache(max_size={self.max_size}, expiration_time={self.expiration_time})" + + +class RedisCache(BaseCacheManager): + """ + A Redis-based cache implementation. + + This cache supports setting an expiration time for cached items. + + Attributes: + expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. + + Example: + + cache = RedisCache(expiration_time=5) + + # setting cache values + cache.set("a", 1) + cache.set("b", 2) + cache["c"] = 3 + + # getting cache values + a = cache.get("a") + b = cache["b"] + """ + + def __init__(self, host="localhost", port=6379, db=0, expiration_time=60 * 60): + """ + Initialize a new RedisCache instance. + + Args: + host (str, optional): Redis host. + port (int, optional): Redis port. + db (int, optional): Redis DB. + expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. + """ + self._client = redis.StrictRedis(host=host, port=port, db=db) + self.expiration_time = expiration_time + + # check connection + def is_connected(self): + """ + Check if the Redis client is connected. """ - previous_client_id = self.current_client_id - self.current_client_id = client_id - self.current_cache = self._cache.setdefault(client_id, {}) try: - yield - finally: - self.current_client_id = previous_client_id - self.current_cache = self._cache.get(self.current_client_id, {}) + self._client.ping() + return True + except redis.exceptions.ConnectionError: + return False - def add(self, name: str, obj: Any, obj_type: str, extension: Optional[str] = None): + def get(self, key): """ - Add an object to the current client's cache. + Retrieve an item from the cache. Args: - name (str): The cache key. - obj (Any): The object to cache. - obj_type (str): The type of the object. - """ - object_extensions = { - "image": "png", - "pandas": "csv", - } - if obj_type in object_extensions: - _extension = object_extensions[obj_type] - else: - _extension = type(obj).__name__.lower() - self.current_cache[name] = { - "obj": obj, - "type": obj_type, - "extension": extension or _extension, - } - self.notify() - - def add_pandas(self, name: str, obj: Any): - """ - Add a pandas DataFrame or Series to the current client's cache. - - Args: - name (str): The cache key. - obj (Any): The pandas DataFrame or Series object. - """ - if isinstance(obj, (pd.DataFrame, pd.Series)): - self.add(name, obj.to_csv(), "pandas", extension="csv") - else: - raise ValueError("Object is not a pandas DataFrame or Series") - - def add_image(self, name: str, obj: Any, extension: str = "png"): - """ - Add a PIL Image to the current client's cache. - - Args: - name (str): The cache key. - obj (Any): The PIL Image object. - """ - if isinstance(obj, Image.Image): - self.add(name, obj, "image", extension=extension) - else: - raise ValueError("Object is not a PIL Image") - - def get(self, name: str): - """ - Get an object from the current client's cache. - - Args: - name (str): The cache key. + key: The key of the item to retrieve. Returns: - The cached object associated with the given cache key. + The value associated with the key, or None if the key is not found. """ - return self.current_cache[name] + value = self._client.get(key) + return pickle.loads(value) if value else None - def get_last(self): + def set(self, key, value): """ - Get the last added item in the current client's cache. + Add an item to the cache. - Returns: - The last added item in the cache. + Args: + key: The key of the item. + value: The value to cache. """ - return list(self.current_cache.values())[-1] + self._client.setex(key, self.expiration_time, pickle.dumps(value)) + def delete(self, key): + """ + Remove an item from the cache. -cache_manager = CacheManager() + Args: + key: The key of the item to remove. + """ + self._client.delete(key) + + def clear(self): + """ + Clear all items from the cache. + """ + self._client.flushdb() + + def __contains__(self, key): + """Check if the key is in the cache.""" + return self._client.exists(key) + + def __getitem__(self, key): + """Retrieve an item from the cache using the square bracket notation.""" + return self.get(key) + + def __setitem__(self, key, value): + """Add an item to the cache using the square bracket notation.""" + self.set(key, value) + + def __delitem__(self, key): + """Remove an item from the cache using the square bracket notation.""" + self.delete(key) + + def __repr__(self): + """Return a string representation of the RedisCache instance.""" + return f"RedisCache(expiration_time={self.expiration_time})" From f65efbc43bf93b4493393dbbc917b17eb2254867 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:37:56 -0300 Subject: [PATCH 010/213] =?UTF-8?q?=F0=9F=94=80=20refactor(base.py):=20mov?= =?UTF-8?q?e=20cache=20settings=20to=20a=20separate=20section=20for=20bett?= =?UTF-8?q?er=20organization=20and=20readability=20=F0=9F=94=80=20refactor?= =?UTF-8?q?(base.py):=20rename=20CACHE=5FTYPE=20variable=20to=20REDIS=5FCA?= =?UTF-8?q?CHE=5FTYPE=20to=20improve=20clarity=20=F0=9F=94=80=20refactor(b?= =?UTF-8?q?ase.py):=20rename=20REDIS=5FHOST,=20REDIS=5FPORT,=20REDIS=5FDB,?= =?UTF-8?q?=20and=20REDIS=5FCACHE=5FEXPIRE=20variables=20to=20improve=20cl?= =?UTF-8?q?arity=20=F0=9F=94=80=20refactor(base.py):=20remove=20unused=20i?= =?UTF-8?q?mport=20statements=20=F0=9F=94=80=20refactor(settings.py):=20re?= =?UTF-8?q?move=20unused=20import=20statements=20=F0=9F=94=80=20refactor(s?= =?UTF-8?q?ettings.py):=20remove=20unused=20code=20and=20comments=20?= =?UTF-8?q?=F0=9F=94=80=20refactor(settings.py):=20remove=20unused=20funct?= =?UTF-8?q?ion=20`save=5Fsettings=5Fto=5Fyaml`=20=F0=9F=94=80=20refactor(s?= =?UTF-8?q?ettings.py):=20remove=20unused=20function=20`update=5Fsettings`?= =?UTF-8?q?=20=F0=9F=94=80=20refactor(settings.py):=20remove=20unused=20fu?= =?UTF-8?q?nction=20`update=5Ffrom=5Fyaml`=20=F0=9F=94=80=20refactor(setti?= =?UTF-8?q?ngs.py):=20remove=20unused=20constant=20`BASE=5FCOMPONENTS=5FPA?= =?UTF-8?q?TH`=20=F0=9F=94=80=20refactor(settings.py):=20remove=20unused?= =?UTF-8?q?=20class=20attributes=20=F0=9F=94=80=20refactor(settings.py):?= =?UTF-8?q?=20remove=20unused=20class=20methods=20=F0=9F=94=80=20refactor(?= =?UTF-8?q?settings.py):=20remove=20unused=20class=20`Settings`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/settings/base.py | 9 + .../langflow/services/settings/settings.py | 171 ------------------ 2 files changed, 9 insertions(+), 171 deletions(-) delete mode 100644 src/backend/langflow/services/settings/settings.py diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py index 1eb2793b3..071e6a296 100644 --- a/src/backend/langflow/services/settings/base.py +++ b/src/backend/langflow/services/settings/base.py @@ -35,6 +35,15 @@ class Settings(BaseSettings): REMOVE_API_KEYS: bool = False COMPONENTS_PATH: List[str] = [] + # cache settings + # if CACHE_TYPE is set to "redis", the following settings are used + CACHE_TYPE: str = "redis" + + REDIS_HOST: str = "localhost" + REDIS_PORT: int = 6379 + REDIS_DB: int = 0 + REDIS_CACHE_EXPIRE: int = 3600 + @validator("DATABASE_URL", pre=True) def set_database_url(cls, value): if not value: diff --git a/src/backend/langflow/services/settings/settings.py b/src/backend/langflow/services/settings/settings.py deleted file mode 100644 index 439b3a1e4..000000000 --- a/src/backend/langflow/services/settings/settings.py +++ /dev/null @@ -1,171 +0,0 @@ -import contextlib -import json -import os -from typing import Optional, List -from pathlib import Path - -import yaml -from pydantic import BaseSettings, root_validator, validator -from langflow.utils.logger import logger - -BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components") - - -class Settings(BaseSettings): - CHAINS: dict = {} - AGENTS: dict = {} - PROMPTS: dict = {} - LLMS: dict = {} - TOOLS: dict = {} - MEMORIES: dict = {} - EMBEDDINGS: dict = {} - VECTORSTORES: dict = {} - DOCUMENTLOADERS: dict = {} - WRAPPERS: dict = {} - RETRIEVERS: dict = {} - TOOLKITS: dict = {} - TEXTSPLITTERS: dict = {} - UTILITIES: dict = {} - OUTPUT_PARSERS: dict = {} - CUSTOM_COMPONENTS: dict = {} - - DEV: bool = False - DATABASE_URL: Optional[str] = None - CACHE: str = "InMemoryCache" - REMOVE_API_KEYS: bool = False - COMPONENTS_PATH: List[str] = [] - - @validator("DATABASE_URL", pre=True) - def set_database_url(cls, value): - if not value: - logger.debug( - "No database_url provided, trying LANGFLOW_DATABASE_URL env variable" - ) - if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"): - value = langflow_database_url - logger.debug("Using LANGFLOW_DATABASE_URL env variable.") - else: - logger.debug("No DATABASE_URL env variable, using sqlite database") - value = "sqlite:///./langflow.db" - - return value - - @validator("COMPONENTS_PATH", pre=True) - def set_components_path(cls, value): - if os.getenv("LANGFLOW_COMPONENTS_PATH"): - logger.debug("Adding LANGFLOW_COMPONENTS_PATH to components_path") - langflow_component_path = os.getenv("LANGFLOW_COMPONENTS_PATH") - if ( - Path(langflow_component_path).exists() - and langflow_component_path not in value - ): - if isinstance(langflow_component_path, list): - for path in langflow_component_path: - if path not in value: - value.append(path) - logger.debug( - f"Extending {langflow_component_path} to components_path" - ) - elif langflow_component_path not in value: - value.append(langflow_component_path) - logger.debug( - f"Appending {langflow_component_path} to components_path" - ) - - if not value: - value = [BASE_COMPONENTS_PATH] - logger.debug("Setting default components path to components_path") - elif BASE_COMPONENTS_PATH not in value: - value.append(BASE_COMPONENTS_PATH) - logger.debug("Adding default components path to components_path") - - logger.debug(f"Components path: {value}") - return value - - class Config: - validate_assignment = True - extra = "ignore" - env_prefix = "LANGFLOW_" - - @root_validator(allow_reuse=True) - def validate_lists(cls, values): - for key, value in values.items(): - if key != "dev" and not value: - values[key] = [] - return values - - def update_from_yaml(self, file_path: str, dev: bool = False): - new_settings = load_settings_from_yaml(file_path) - self.CHAINS = new_settings.CHAINS or {} - self.AGENTS = new_settings.AGENTS or {} - self.PROMPTS = new_settings.PROMPTS or {} - self.LLMS = new_settings.LLMS or {} - self.TOOLS = new_settings.TOOLS or {} - self.MEMORIES = new_settings.MEMORIES or {} - self.WRAPPERS = new_settings.WRAPPERS or {} - self.TOOLKITS = new_settings.TOOLKITS or {} - self.TEXTSPLITTERS = new_settings.TEXTSPLITTERS or {} - self.UTILITIES = new_settings.UTILITIES or {} - self.EMBEDDINGS = new_settings.EMBEDDINGS or {} - self.VECTORSTORES = new_settings.VECTORSTORES or {} - self.DOCUMENTLOADERS = new_settings.DOCUMENTLOADERS or {} - self.RETRIEVERS = new_settings.RETRIEVERS or {} - self.OUTPUT_PARSERS = new_settings.OUTPUT_PARSERS or {} - self.CUSTOM_COMPONENTS = new_settings.CUSTOM_COMPONENTS or {} - self.COMPONENTS_PATH = new_settings.COMPONENTS_PATH or [] - self.DEV = dev - - def update_settings(self, **kwargs): - logger.debug("Updating settings") - for key, value in kwargs.items(): - # value may contain sensitive information, so we don't want to log it - if not hasattr(self, key): - logger.debug(f"Key {key} not found in settings") - continue - logger.debug(f"Updating {key}") - if isinstance(getattr(self, key), list): - # value might be a '[something]' string - with contextlib.suppress(json.decoder.JSONDecodeError): - value = json.loads(str(value)) - if isinstance(value, list): - for item in value: - if item not in getattr(self, key): - getattr(self, key).append(item) - logger.debug(f"Extended {key}") - else: - getattr(self, key).append(value) - logger.debug(f"Appended {key}") - - else: - setattr(self, key, value) - logger.debug(f"Updated {key}") - logger.debug(f"{key}: {getattr(self, key)}") - - -def save_settings_to_yaml(settings: Settings, file_path: str): - with open(file_path, "w") as f: - settings_dict = settings.dict() - yaml.dump(settings_dict, f) - - -def load_settings_from_yaml(file_path: str) -> Settings: - # Check if a string is a valid path or a file name - if "/" not in file_path: - # Get current path - current_path = os.path.dirname(os.path.abspath(__file__)) - - file_path = os.path.join(current_path, file_path) - - with open(file_path, "r") as f: - settings_dict = yaml.safe_load(f) - settings_dict = {k.upper(): v for k, v in settings_dict.items()} - - for key in settings_dict: - if key not in Settings.__fields__.keys(): - raise KeyError(f"Key {key} not found in settings") - logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}") - - return Settings(**settings_dict) - - -settings = load_settings_from_yaml("config.yaml") From 1a51e90c43516915039c7d28acae0418b4d5a957 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:38:31 -0300 Subject: [PATCH 011/213] =?UTF-8?q?=F0=9F=90=9B=20fix(chat.py):=20remove?= =?UTF-8?q?=20unused=20import=20and=20variable=20'flow=5Fdata=5Fstore'=20t?= =?UTF-8?q?o=20improve=20code=20readability=20=E2=9C=A8=20feat(chat.py):?= =?UTF-8?q?=20add=20support=20for=20cache=5Fmanager=20to=20handle=20chat?= =?UTF-8?q?=20history=20and=20cache=20langchain=5Fobject=20=F0=9F=86=95=20?= =?UTF-8?q?feat(chat/manager.py):=20add=20ChatManager=20class=20to=20handl?= =?UTF-8?q?e=20websocket=20connections=20and=20chat=20history=20?= =?UTF-8?q?=F0=9F=86=95=20feat(chat/manager.py):=20add=20ChatHistory=20cla?= =?UTF-8?q?ss=20to=20manage=20chat=20history=20for=20each=20client=20?= =?UTF-8?q?=F0=9F=86=95=20feat(chat/manager.py):=20add=20methods=20to=20ha?= =?UTF-8?q?ndle=20websocket=20connections,=20send=20messages,=20and=20proc?= =?UTF-8?q?ess=20chat=20messages=20=F0=9F=86=95=20feat(chat/manager.py):?= =?UTF-8?q?=20add=20method=20to=20set=20cache=20for=20a=20client=20and=20h?= =?UTF-8?q?andle=20websocket=20communication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/chat.py | 21 ++- src/backend/langflow/chat/manager.py | 217 +++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 11 deletions(-) create mode 100644 src/backend/langflow/chat/manager.py diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index 611407e8d..2b985584e 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -6,19 +6,16 @@ from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, St from langflow.services import service_manager, ServiceType from langflow.graph.graph.base import Graph from langflow.utils.logger import logger -from cachetools import LRUCache router = APIRouter(tags=["Chat"]) -flow_data_store: LRUCache = LRUCache(maxsize=10) - @router.websocket("/chat/{client_id}") async def chat(client_id: str, websocket: WebSocket): """Websocket endpoint for chat.""" try: chat_manager = service_manager.get(ServiceType.CHAT_MANAGER) - if client_id in chat_manager.in_memory_cache: + if client_id in chat_manager.cache_manager: await chat_manager.handle_websocket(client_id, websocket) else: # We accept the connection but close it immediately @@ -34,23 +31,23 @@ async def chat(client_id: str, websocket: WebSocket): @router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201) async def init_build(graph_data: dict, flow_id: str): """Initialize the build by storing graph data and returning a unique session ID.""" - + flow_data_store = service_manager.get(ServiceType.CACHE_MANAGER) try: if flow_id is None: raise ValueError("No ID provided") # Check if already building if ( flow_id in flow_data_store - and flow_data_store[flow_id]["status"] == BuildStatus.IN_PROGRESS + and isinstance(flow_data_store[flow_id], dict) + and flow_data_store[flow_id].get("status") == BuildStatus.IN_PROGRESS ): return InitResponse(flowId=flow_id) # Delete from cache if already exists chat_manager = service_manager.get(ServiceType.CHAT_MANAGER) - if flow_id in chat_manager.in_memory_cache: - with chat_manager.in_memory_cache._lock: - chat_manager.in_memory_cache.delete(flow_id) - logger.debug(f"Deleted flow {flow_id} from cache") + if flow_id in chat_manager.cache_manager: + chat_manager.cache_manager.delete(flow_id) + logger.debug(f"Deleted flow {flow_id} from cache") flow_data_store[flow_id] = { "graph_data": graph_data, "status": BuildStatus.STARTED, @@ -65,6 +62,7 @@ async def init_build(graph_data: dict, flow_id: str): @router.get("/build/{flow_id}/status", response_model=BuiltResponse) async def build_status(flow_id: str): """Check the flow_id is in the flow_data_store.""" + flow_data_store = service_manager.get(ServiceType.CACHE_MANAGER) try: built = ( flow_id in flow_data_store @@ -83,6 +81,7 @@ async def build_status(flow_id: str): @router.get("/build/stream/{flow_id}", response_class=StreamingResponse) async def stream_build(flow_id: str): """Stream the build process based on stored flow data.""" + flow_data_store = service_manager.get(ServiceType.CACHE_MANAGER) async def event_stream(flow_id): final_response = {"end_of_stream": True} @@ -163,7 +162,7 @@ async def stream_build(flow_id: str): } yield str(StreamData(event="message", data=input_keys_response)) chat_manager = service_manager.get(ServiceType.CHAT_MANAGER) - chat_manager.set_cache(flow_id, langchain_object) + chat_manager.set_cache(f"{flow_id}_chat", langchain_object) # We need to reset the chat history chat_manager.chat_history.empty_history(flow_id) flow_data_store[flow_id]["status"] = BuildStatus.SUCCESS diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py new file mode 100644 index 000000000..6c78a2d77 --- /dev/null +++ b/src/backend/langflow/chat/manager.py @@ -0,0 +1,217 @@ +from collections import defaultdict +from fastapi import WebSocket, status +from langflow.api.v1.schemas import ChatMessage, ChatResponse, FileResponse +from langflow.cache import cache_manager +from langflow.cache.manager import Subject +from langflow.chat.utils import process_graph +from langflow.interface.utils import pil_to_base64 +from langflow.utils.logger import logger + + +import asyncio +import json +from typing import Any, Dict, List + +from langflow.cache.flow import InMemoryCache + + +class ChatHistory(Subject): + def __init__(self): + super().__init__() + self.history: Dict[str, List[ChatMessage]] = defaultdict(list) + + def add_message(self, client_id: str, message: ChatMessage): + """Add a message to the chat history.""" + + self.history[client_id].append(message) + + if not isinstance(message, FileResponse): + self.notify() + + def get_history(self, client_id: str, filter_messages=True) -> List[ChatMessage]: + """Get the chat history for a client.""" + if history := self.history.get(client_id, []): + if filter_messages: + return [msg for msg in history if msg.type not in ["start", "stream"]] + return history + else: + return [] + + def empty_history(self, client_id: str): + """Empty the chat history for a client.""" + self.history[client_id] = [] + + +class ChatManager: + def __init__(self): + self.active_connections: Dict[str, WebSocket] = {} + self.chat_history = ChatHistory() + self.cache_manager = cache_manager + self.cache_manager.attach(self.update) + self.in_memory_cache = InMemoryCache() + + def on_chat_history_update(self): + """Send the last chat message to the client.""" + client_id = self.cache_manager.current_client_id + if client_id in self.active_connections: + chat_response = self.chat_history.get_history( + client_id, filter_messages=False + )[-1] + if chat_response.is_bot: + # Process FileResponse + if isinstance(chat_response, FileResponse): + # If data_type is pandas, convert to csv + if chat_response.data_type == "pandas": + chat_response.data = chat_response.data.to_csv() + elif chat_response.data_type == "image": + # Base64 encode the image + chat_response.data = pil_to_base64(chat_response.data) + # get event loop + loop = asyncio.get_event_loop() + + coroutine = self.send_json(client_id, chat_response) + asyncio.run_coroutine_threadsafe(coroutine, loop) + + def update(self): + if self.cache_manager.current_client_id in self.active_connections: + self.last_cached_object_dict = self.cache_manager.get_last() + # Add a new ChatResponse with the data + chat_response = FileResponse( + message=None, + type="file", + data=self.last_cached_object_dict["obj"], + data_type=self.last_cached_object_dict["type"], + ) + + self.chat_history.add_message( + self.cache_manager.current_client_id, chat_response + ) + + async def connect(self, client_id: str, websocket: WebSocket): + await websocket.accept() + self.active_connections[client_id] = websocket + + def disconnect(self, client_id: str): + self.active_connections.pop(client_id, None) + + async def send_message(self, client_id: str, message: str): + websocket = self.active_connections[client_id] + await websocket.send_text(message) + + async def send_json(self, client_id: str, message: ChatMessage): + websocket = self.active_connections[client_id] + await websocket.send_json(message.dict()) + + async def close_connection(self, client_id: str, code: int, reason: str): + if websocket := self.active_connections[client_id]: + try: + await websocket.close(code=code, reason=reason) + self.disconnect(client_id) + except RuntimeError as exc: + # This is to catch the following error: + # Unexpected ASGI message 'websocket.close', after sending 'websocket.close' + if "after sending" in str(exc): + logger.error(f"Error closing connection: {exc}") + + async def process_message( + self, client_id: str, payload: Dict, langchain_object: Any + ): + # Process the graph data and chat message + chat_inputs = payload.pop("inputs", {}) + chat_inputs = ChatMessage(message=chat_inputs) + self.chat_history.add_message(client_id, chat_inputs) + + # graph_data = payload + start_resp = ChatResponse(message=None, type="start", intermediate_steps="") + await self.send_json(client_id, start_resp) + + # is_first_message = len(self.chat_history.get_history(client_id=client_id)) <= 1 + # Generate result and thought + try: + logger.debug("Generating result and thought") + + result, intermediate_steps = await process_graph( + langchain_object=langchain_object, + chat_inputs=chat_inputs, + websocket=self.active_connections[client_id], + ) + except Exception as e: + # Log stack trace + logger.exception(e) + self.chat_history.empty_history(client_id) + raise e + # Send a response back to the frontend, if needed + intermediate_steps = intermediate_steps or "" + history = self.chat_history.get_history(client_id, filter_messages=False) + file_responses = [] + if history: + # Iterate backwards through the history + for msg in reversed(history): + if isinstance(msg, FileResponse): + if msg.data_type == "image": + # Base64 encode the image + if isinstance(msg.data, str): + continue + msg.data = pil_to_base64(msg.data) + file_responses.append(msg) + if msg.type == "start": + break + + response = ChatResponse( + message=result, + intermediate_steps=intermediate_steps.strip(), + type="end", + files=file_responses, + ) + await self.send_json(client_id, response) + self.chat_history.add_message(client_id, response) + + def set_cache(self, client_id: str, langchain_object: Any) -> bool: + """ + Set the cache for a client. + """ + client_id = f"{client_id}_chat" + self.cache_manager.set(client_id, langchain_object) + return client_id in self.cache_manager + + async def handle_websocket(self, client_id: str, websocket: WebSocket): + await self.connect(client_id, websocket) + + try: + chat_history = self.chat_history.get_history(client_id) + # iterate and make BaseModel into dict + chat_history = [chat.dict() for chat in chat_history] + await websocket.send_json(chat_history) + + while True: + json_payload = await websocket.receive_json() + try: + payload = json.loads(json_payload) + except TypeError: + payload = json_payload + if "clear_history" in payload: + self.chat_history.history[client_id] = [] + continue + + with self.cache_manager.set_client_id(client_id): + langchain_object = self.in_memory_cache.get(f"{client_id}_chat") + await self.process_message(client_id, payload, langchain_object) + + except Exception as exc: + # Handle any exceptions that might occur + logger.error(f"Error handling websocket: {exc}") + await self.close_connection( + client_id=client_id, + code=status.WS_1011_INTERNAL_ERROR, + reason=str(exc)[:120], + ) + finally: + try: + await self.close_connection( + client_id=client_id, + code=status.WS_1000_NORMAL_CLOSURE, + reason="Client disconnected", + ) + except Exception as exc: + logger.error(f"Error closing connection: {exc}") + self.disconnect(client_id) From 0833b00fdc5b6dd0e67247ef5968c145e1a86316 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:39:01 -0300 Subject: [PATCH 012/213] =?UTF-8?q?=F0=9F=94=A7=20chore(celery=5Fapp.py):?= =?UTF-8?q?=20refactor=20celery=5Fapp=20to=20use=20a=20separate=20function?= =?UTF-8?q?=20make=5Fcelery=20for=20improved=20modularity=20and=20readabil?= =?UTF-8?q?ity=20=F0=9F=94=A7=20chore(celeryconfig.py):=20update=20broker?= =?UTF-8?q?=5Furl=20and=20result=5Fbackend=20to=20use=20LANGFLOW=5FREDIS?= =?UTF-8?q?=5FHOST=20and=20LANGFLOW=5FREDIS=5FPORT=20environment=20variabl?= =?UTF-8?q?es=20if=20available,=20fallback=20to=20default=20values=20if=20?= =?UTF-8?q?not?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/core/celery_app.py | 14 +++++++++----- src/backend/langflow/core/celeryconfig.py | 10 ++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/backend/langflow/core/celery_app.py b/src/backend/langflow/core/celery_app.py index 7b89a05ab..85223491e 100644 --- a/src/backend/langflow/core/celery_app.py +++ b/src/backend/langflow/core/celery_app.py @@ -1,7 +1,11 @@ from celery import Celery -celery_app = Celery( - "langflow", broker="redis://queue:6379/0", backend="redis://queue:6379/0" -) -# command: celery -A langflow.worker.celery_app worker --loglevel=INFO -celery_app.conf.task_routes = {"langflow.worker.tasks.*": {"queue": "langflow"}} + +def make_celery(app_name: str): + celery_app = Celery(app_name) + celery_app.config_from_object("langflow.core.celeryconfig") + celery_app.conf.task_routes = {"langflow.worker.tasks.*": {"queue": "langflow"}} + return celery_app + + +celery_app = make_celery("langflow") diff --git a/src/backend/langflow/core/celeryconfig.py b/src/backend/langflow/core/celeryconfig.py index 647a7f93d..d7827b713 100644 --- a/src/backend/langflow/core/celeryconfig.py +++ b/src/backend/langflow/core/celeryconfig.py @@ -1,7 +1,13 @@ # celeryconfig.py import os -broker_url = os.environ.get("BROKER_URL", "redis://localhost:6379/0") -result_backend = os.environ.get("RESULT_BACKEND", "redis://localhost:6379/0") +langflow_redis_host = os.environ.get("LANGFLOW_REDIS_HOST") +langflow_redis_port = os.environ.get("LANGFLOW_REDIS_PORT") +if langflow_redis_host and langflow_redis_port: + broker_url = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" + result_backend = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" +else: + broker_url = os.environ.get("BROKER_URL", "redis://localhost:6379/0") + result_backend = os.environ.get("RESULT_BACKEND", "redis://localhost:6379/0") # tasks should be json or pickle accept_content = ["json", "pickle"] From f8f99c68815336f89434d92a95f99a1e410622f7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:40:12 -0300 Subject: [PATCH 013/213] =?UTF-8?q?=F0=9F=90=9B=20fix(utils.py):=20rename?= =?UTF-8?q?=20cache=5Ftype=20variable=20to=20langchain=5Fcache=5Ftype=20fo?= =?UTF-8?q?r=20clarity=20=F0=9F=90=9B=20fix(manager.py):=20add=20test=20co?= =?UTF-8?q?nnections=20to=20cache=20and=20database=20managers=20for=20init?= =?UTF-8?q?ialization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/utils.py | 6 ++++-- src/backend/langflow/services/manager.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/interface/utils.py b/src/backend/langflow/interface/utils.py index 1fddbf80f..822e32c35 100644 --- a/src/backend/langflow/interface/utils.py +++ b/src/backend/langflow/interface/utils.py @@ -77,8 +77,10 @@ def set_langchain_cache(settings): import langchain from langflow.interface.importing.utils import import_class - cache_type = os.getenv("LANGFLOW_LANGCHAIN_CACHE") - cache_class = import_class(f"langchain.cache.{cache_type or settings.CACHE}") + langchain_cache_type = os.getenv("LANGFLOW_LANGCHAIN_CACHE") + cache_class = import_class( + f"langchain.cache.{langchain_cache_type or settings.CACHE}" + ) logger.debug(f"Setting up LLM caching with {cache_class.__name__}") langchain.llm_cache = cache_class() diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index 1606b3a82..1f7e4b2bd 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -85,3 +85,8 @@ def initialize_services(): service_manager.register_factory(database_factory.DatabaseManagerFactory()) service_manager.register_factory(cache_factory.CacheManagerFactory()) service_manager.register_factory(chat_factory.ChatManagerFactory()) + + # Test cache connection + service_manager.get(ServiceType.CACHE_MANAGER) + # Test database connection + service_manager.get(ServiceType.DATABASE_MANAGER) From 64292aa9978d4574dd1c61c1ab9a5ad661450aa8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:40:42 -0300 Subject: [PATCH 014/213] =?UTF-8?q?=E2=9C=A8=20feat(cache.py):=20add=20cac?= =?UTF-8?q?he=20manager=20class=20to=20manage=20cache=20for=20different=20?= =?UTF-8?q?clients=20and=20notify=20observers=20on=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/chat/cache.py | 153 ++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/backend/langflow/services/chat/cache.py diff --git a/src/backend/langflow/services/chat/cache.py b/src/backend/langflow/services/chat/cache.py new file mode 100644 index 000000000..ce9a338ef --- /dev/null +++ b/src/backend/langflow/services/chat/cache.py @@ -0,0 +1,153 @@ +from contextlib import contextmanager +from typing import Any, Awaitable, Callable, List, Optional +from langflow.services.base import Service + +import pandas as pd +from PIL import Image + + +class Subject: + """Base class for implementing the observer pattern.""" + + def __init__(self): + self.observers: List[Callable[[], None]] = [] + + def attach(self, observer: Callable[[], None]): + """Attach an observer to the subject.""" + self.observers.append(observer) + + def detach(self, observer: Callable[[], None]): + """Detach an observer from the subject.""" + self.observers.remove(observer) + + def notify(self): + """Notify all observers about an event.""" + for observer in self.observers: + if observer is None: + continue + observer() + + +class AsyncSubject: + """Base class for implementing the async observer pattern.""" + + def __init__(self): + self.observers: List[Callable[[], Awaitable]] = [] + + def attach(self, observer: Callable[[], Awaitable]): + """Attach an observer to the subject.""" + self.observers.append(observer) + + def detach(self, observer: Callable[[], Awaitable]): + """Detach an observer from the subject.""" + self.observers.remove(observer) + + async def notify(self): + """Notify all observers about an event.""" + for observer in self.observers: + if observer is None: + continue + await observer() + + +class CacheManager(Subject, Service): + """Manages cache for different clients and notifies observers on changes.""" + + name = "cache_manager" + + def __init__(self): + super().__init__() + self._cache = {} + self.current_client_id = None + self.current_cache = {} + + @contextmanager + def set_client_id(self, client_id: str): + """ + Context manager to set the current client_id and associated cache. + + Args: + client_id (str): The client identifier. + """ + previous_client_id = self.current_client_id + self.current_client_id = client_id + self.current_cache = self._cache.setdefault(client_id, {}) + try: + yield + finally: + self.current_client_id = previous_client_id + self.current_cache = self._cache.get(self.current_client_id, {}) + + def add(self, name: str, obj: Any, obj_type: str, extension: Optional[str] = None): + """ + Add an object to the current client's cache. + + Args: + name (str): The cache key. + obj (Any): The object to cache. + obj_type (str): The type of the object. + """ + object_extensions = { + "image": "png", + "pandas": "csv", + } + if obj_type in object_extensions: + _extension = object_extensions[obj_type] + else: + _extension = type(obj).__name__.lower() + self.current_cache[name] = { + "obj": obj, + "type": obj_type, + "extension": extension or _extension, + } + self.notify() + + def add_pandas(self, name: str, obj: Any): + """ + Add a pandas DataFrame or Series to the current client's cache. + + Args: + name (str): The cache key. + obj (Any): The pandas DataFrame or Series object. + """ + if isinstance(obj, (pd.DataFrame, pd.Series)): + self.add(name, obj.to_csv(), "pandas", extension="csv") + else: + raise ValueError("Object is not a pandas DataFrame or Series") + + def add_image(self, name: str, obj: Any, extension: str = "png"): + """ + Add a PIL Image to the current client's cache. + + Args: + name (str): The cache key. + obj (Any): The PIL Image object. + """ + if isinstance(obj, Image.Image): + self.add(name, obj, "image", extension=extension) + else: + raise ValueError("Object is not a PIL Image") + + def get(self, name: str): + """ + Get an object from the current client's cache. + + Args: + name (str): The cache key. + + Returns: + The cached object associated with the given cache key. + """ + return self.current_cache[name] + + def get_last(self): + """ + Get the last added item in the current client's cache. + + Returns: + The last added item in the cache. + """ + return list(self.current_cache.values())[-1] + + +cache_manager = CacheManager() From 561e0482788afa17e0134229579ef30e4d6dcd32 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:41:08 -0300 Subject: [PATCH 015/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20fix?= =?UTF-8?q?=20import=20statements=20and=20variable=20names=20for=20better?= =?UTF-8?q?=20organization=20and=20readability=20=E2=9C=A8=20feat(manager.?= =?UTF-8?q?py):=20add=20support=20for=20chat=20cache=20and=20attach=20it?= =?UTF-8?q?=20to=20the=20chat=20manager=20for=20better=20chat=20history=20?= =?UTF-8?q?management=20=F0=9F=94=A7=20chore(manager.py):=20change=20clien?= =?UTF-8?q?t=5Fid=20format=20in=20set=5Fcache=20method=20to=20avoid=20conf?= =?UTF-8?q?licts=20with=20existing=20cache=20keys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/chat/manager.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index a49f48273..76790cbb4 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -2,19 +2,17 @@ from collections import defaultdict from fastapi import WebSocket, status from langflow.api.v1.schemas import ChatMessage, ChatResponse, FileResponse from langflow.services.base import Service -from langflow.services import service_manager -from langflow.services.cache.manager import Subject +from langflow.services.chat.cache import Subject from langflow.services.chat.utils import process_graph from langflow.interface.utils import pil_to_base64 -from langflow.services.schema import ServiceType from langflow.utils.logger import logger - +from .cache import cache_manager import asyncio import json from typing import Any, Dict, List -from langflow.services.cache.flow import InMemoryCache +from langflow.services import service_manager, ServiceType class ChatHistory(Subject): @@ -50,13 +48,13 @@ class ChatManager(Service): def __init__(self): self.active_connections: Dict[str, WebSocket] = {} self.chat_history = ChatHistory() + self.chat_cache = cache_manager + self.chat_cache.attach(self.update) self.cache_manager = service_manager.get(ServiceType.CACHE_MANAGER) - self.cache_manager.attach(self.update) - self.in_memory_cache = InMemoryCache() def on_chat_history_update(self): """Send the last chat message to the client.""" - client_id = self.cache_manager.current_client_id + client_id = self.chat_cache.current_client_id if client_id in self.active_connections: chat_response = self.chat_history.get_history( client_id, filter_messages=False @@ -77,8 +75,8 @@ class ChatManager(Service): asyncio.run_coroutine_threadsafe(coroutine, loop) def update(self): - if self.cache_manager.current_client_id in self.active_connections: - self.last_cached_object_dict = self.cache_manager.get_last() + if self.chat_cache.current_client_id in self.active_connections: + self.last_cached_object_dict = self.chat_cache.get_last() # Add a new ChatResponse with the data chat_response = FileResponse( message=None, @@ -88,7 +86,7 @@ class ChatManager(Service): ) self.chat_history.add_message( - self.cache_manager.current_client_id, chat_response + self.chat_cache.current_client_id, chat_response ) async def connect(self, client_id: str, websocket: WebSocket): @@ -174,9 +172,12 @@ class ChatManager(Service): """ Set the cache for a client. """ + # client_id is the flow id but that already exists in the cache + # so we need to change it to something else - self.in_memory_cache.set(client_id, langchain_object) - return client_id in self.in_memory_cache + client_id = f"{client_id}_chat" if "_chat" not in client_id else client_id + self.cache_manager.set(client_id, langchain_object) + return client_id in self.cache_manager async def handle_websocket(self, client_id: str, websocket: WebSocket): await self.connect(client_id, websocket) @@ -197,8 +198,8 @@ class ChatManager(Service): self.chat_history.history[client_id] = [] continue - with self.cache_manager.set_client_id(client_id): - langchain_object = self.in_memory_cache.get(client_id) + with self.chat_cache.set_client_id(client_id): + langchain_object = self.cache_manager.get(f"{client_id}_chat") await self.process_message(client_id, payload, langchain_object) except Exception as exc: From 0ed1165e530314c70730c45a08ae17860f65355c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:43:14 -0300 Subject: [PATCH 016/213] =?UTF-8?q?=F0=9F=90=B3=20chore(docker-compose.cel?= =?UTF-8?q?ery.yml):=20add=20dependencies=20and=20environment=20variables?= =?UTF-8?q?=20for=20redis=20cache=20and=20queue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 fix(docker-compose.celery.yml): update uvicorn command to include debug log level 🔧 fix(docker-compose.celery.yml): update flower command to include redis broker URL 🔧 fix(docker-compose.celery.yml): add dependencies and environment variables for redis cache and queue in frontend service --- docker-compose.celery.yml | 42 +++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/docker-compose.celery.yml b/docker-compose.celery.yml index d648b331b..a568be593 100644 --- a/docker-compose.celery.yml +++ b/docker-compose.celery.yml @@ -5,11 +5,19 @@ services: build: context: ./ dockerfile: base.Dockerfile + depends_on: + - queue + environment: + - LANGFLOW_CACHE_TYPE=redis + - LANGFLOW_REDIS_HOST=queue + - LANGFLOW_REDIS_PORT=6379 + - LANGFLOW_REDIS_DB=0 + - LANGFLOW_REDIS_EXPIRE=3600 ports: - "7860:7860" volumes: - ./:/app - command: bash -c "uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload" + command: bash -c "uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level debug" queue: image: redis:latest @@ -19,13 +27,16 @@ services: celeryworker: depends_on: - queue - env_file: - - .env - # environment: - # - SERVER_NAME=${DOMAIN?Variable not set} - # - SERVER_HOST=https://${DOMAIN?Variable not set} - # # Allow explicit env var override for tests - # - SMTP_HOST=${SMTP_HOST?Variable not set} + environment: + - LANGFLOW_CACHE_TYPE=redis + - LANGFLOW_REDIS_HOST=queue + - LANGFLOW_REDIS_PORT=6379 + - LANGFLOW_REDIS_DB=0 + - LANGFLOW_REDIS_EXPIRE=3600 + - BROKER_URL=redis://queue:6379/0 + - RESULT_BACKEND=redis://queue:6379/0 + - C_FORCE_ROOT=true # ! Only for development + build: context: ./ dockerfile: base.Dockerfile @@ -34,12 +45,18 @@ services: flower: networks: - default + depends_on: + - queue build: context: ./ dockerfile: base.Dockerfile - env_file: - - .env - command: celery -A langflow.worker.celery_app flower --port=5555 --broker=redis://queue:6379/0 + environment: + - LANGFLOW_CACHE_TYPE=redis + - LANGFLOW_REDIS_HOST=queue + - LANGFLOW_REDIS_PORT=6379 + - LANGFLOW_REDIS_DB=0 + - LANGFLOW_REDIS_EXPIRE=3600 + command: celery -A langflow.worker.celery_app --broker=redis://queue:6379/0 flower --port=5555 ports: - "5555:5555" @@ -49,6 +66,9 @@ services: dockerfile: ./dev.Dockerfile args: - BACKEND_URL=http://backend:7860 + + depends_on: + - backend environment: - VITE_PROXY_TARGET=http://backend:7860 ports: From 5f0c014dcb0b27ee6c6402dacf68215f02ec8a0b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:45:34 -0300 Subject: [PATCH 017/213] =?UTF-8?q?=F0=9F=93=A6=20chore(pyproject.toml):?= =?UTF-8?q?=20update=20dependencies=20and=20extras?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔒 chore(pyproject.toml): update celery, redis, and flower versions and make them optional dependencies 🔀 chore(pyproject.toml): add celery, redis, and flower to the deploy extra dependencies --- poetry.lock | 27 ++++++++++++++------------- pyproject.toml | 9 +++++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index a7dc09759..f38089432 100644 --- a/poetry.lock +++ b/poetry.lock @@ -167,7 +167,7 @@ tz = ["python-dateutil"] name = "amqp" version = "5.1.1" description = "Low-level AMQP client for Python (fork of amqplib)." -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, @@ -371,7 +371,7 @@ lxml = ["lxml"] name = "billiard" version = "4.1.0" description = "Python multiprocessing fork with improvements and bugfixes" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "billiard-4.1.0-py3-none-any.whl", hash = "sha256:0f50d6be051c6b2b75bfbc8bfd85af195c5739c281d3f5b86a5640c65563614a"}, @@ -457,7 +457,7 @@ files = [ name = "celery" version = "5.3.1" description = "Distributed Task Queue." -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "celery-5.3.1-py3-none-any.whl", hash = "sha256:27f8f3f3b58de6e0ab4f174791383bbd7445aff0471a43e99cfd77727940753f"}, @@ -472,6 +472,7 @@ click-plugins = ">=1.1.1" click-repl = ">=0.2.0" kombu = ">=5.3.1,<6.0" python-dateutil = ">=2.8.2" +redis = {version = ">=4.5.2,<4.5.5 || >4.5.5", optional = true, markers = "extra == \"redis\""} tzdata = ">=2022.7" vine = ">=5.0.0,<6.0" @@ -737,7 +738,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "click-didyoumean" version = "0.3.0" description = "Enables git-like *did-you-mean* feature in click" -optional = false +optional = true python-versions = ">=3.6.2,<4.0.0" files = [ {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, @@ -765,7 +766,7 @@ click = "*" name = "click-plugins" version = "1.1.1" description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -optional = false +optional = true python-versions = "*" files = [ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, @@ -782,7 +783,7 @@ dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] name = "click-repl" version = "0.3.0" description = "REPL plugin for Click" -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, @@ -1570,7 +1571,7 @@ files = [ name = "flower" version = "2.0.1" description = "Celery Flower" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "flower-2.0.1-py2.py3-none-any.whl", hash = "sha256:9db2c621eeefbc844c8dd88be64aef61e84e2deb29b271e02ab2b5b9f01068e2"}, @@ -2553,7 +2554,7 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve name = "humanize" version = "4.7.0" description = "Python humanize utilities" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "humanize-4.7.0-py3-none-any.whl", hash = "sha256:df7c429c2d27372b249d3f26eb53b07b166b661326e0325793e0a988082e3889"}, @@ -3030,7 +3031,7 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "kombu" version = "5.3.1" description = "Messaging library for Python." -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "kombu-5.3.1-py3-none-any.whl", hash = "sha256:48ee589e8833126fd01ceaa08f8a2041334e9f5894e5763c8486a550454551e9"}, @@ -5808,7 +5809,7 @@ websockets = ">=10.3,<11.0" name = "redis" version = "4.6.0" description = "Python client for Redis database and key-value store" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"}, @@ -7354,7 +7355,7 @@ files = [ name = "vine" version = "5.0.0" description = "Promises, promises, promises." -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, @@ -7846,10 +7847,10 @@ cffi = ["cffi (>=1.11)"] [extras] all = [] -deploy = ["langchain-serve"] +deploy = ["celery", "flower", "langchain-serve", "redis"] local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "f19617ccd82d10dc15746a965b1dc74e3c79e6755f255ab1d03a1076e6c38e5f" +content-hash = "00a3de3cc8916619f8434289b23a51e768b7b7c74f33e29c0ea3c61688ea3874" diff --git a/pyproject.toml b/pyproject.toml index 806f93148..b3e7170f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,12 +77,13 @@ psycopg = "^3.1.9" psycopg-binary = "^3.1.9" fastavro = "^1.8.0" langchain-experimental = "^0.0.8" -celery = "^5.3.1" -redis = "^4.6.0" -flower = "^2.0.1" +celery = { extras = ["redis"], version = "^5.3.1", optional = true } +redis = { version = "^4.6.0", optional = true } +flower = { version = "^2.0.0", optional = true } alembic = "^1.11.2" metaphor-python = "^0.1.11" + [tool.poetry.group.dev.dependencies] black = "^23.1.0" ipykernel = "^6.21.2" @@ -100,7 +101,7 @@ types-pyyaml = "^6.0.12.8" [tool.poetry.extras] -deploy = ["langchain-serve"] +deploy = ["langchain-serve", "celery", "redis", "flower"] local = ["llama-cpp-python", "sentence-transformers", "ctransformers"] all = ["deploy", "local"] From 4bf4768ca78b7fef1e0e5549e0c205b69f100c6a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:45:59 -0300 Subject: [PATCH 018/213] =?UTF-8?q?=F0=9F=94=A7=20chore(base.Dockerfile):?= =?UTF-8?q?=20update=20poetry=20install=20command=20to=20exclude=20dev=20d?= =?UTF-8?q?ependencies=20and=20include=20deploy=20dependencies=20for=20pro?= =?UTF-8?q?duction=20deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base.Dockerfile b/base.Dockerfile index 23d0262b5..2306f7bf6 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -68,7 +68,7 @@ COPY ./src/backend ./src/backend COPY README.md . # install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally RUN --mount=type=cache,target=/root/.cache \ - poetry install --without=dev + poetry install --without dev --with deploy ################################ From 37773e0ca122cb9531bbe479531573d1600b5ebf Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 13 Aug 2023 23:46:26 -0300 Subject: [PATCH 019/213] =?UTF-8?q?=F0=9F=93=9D=20chore(.env.example):=20a?= =?UTF-8?q?dd=20documentation=20for=20LANGFLOW=5FCACHE=5FTYPE=20environmen?= =?UTF-8?q?t=20variable=20and=20its=20possible=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.env.example b/.env.example index 6c6d2f667..44fcae3da 100644 --- a/.env.example +++ b/.env.example @@ -45,3 +45,14 @@ LANGFLOW_OPEN_BROWSER= # Values: true, false # Example: LANGFLOW_REMOVE_API_KEYS=false LANGFLOW_REMOVE_API_KEYS= + +# Whether to use RedisCache or InMemoryCache +# Values: memory, redis +# Example: LANGFLOW_CACHE_TYPE=memory +# If you want to use redis then the following environment variables must be set: +# LANGFLOW_REDIS_HOST (default: localhost) +# LANGFLOW_REDIS_PORT (default: 6379) +# LANGFLOW_REDIS_DB (default: 0) +# LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600) +LANGFLOW_CACHE_TYPE= + From 5df7341eec124f2f1373037e3b064ed38b08ed4b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 14 Aug 2023 12:23:21 -0300 Subject: [PATCH 020/213] =?UTF-8?q?=F0=9F=90=9B=20fix(chat.py):=20rename?= =?UTF-8?q?=20flow=5Fdata=5Fstore=20variable=20to=20cache=5Fmanager=20for?= =?UTF-8?q?=20better=20clarity=20and=20consistency=20=E2=9C=A8=20feat(chat?= =?UTF-8?q?.py):=20update=20references=20to=20flow=5Fdata=5Fstore=20to=20c?= =?UTF-8?q?ache=5Fmanager=20for=20improved=20semantics=20and=20readability?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(chat.py):=20fix=20incorrect=20cache=20key?= =?UTF-8?q?=20in=20chat=5Fmanager.set=5Fcache()=20method=20call=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(chat.py):=20fix=20incorrect=20cache=20key=20?= =?UTF-8?q?in=20chat=5Fmanager.chat=5Fhistory.empty=5Fhistory()=20method?= =?UTF-8?q?=20call?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/chat.py | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index 2b985584e..2cec3b898 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -31,15 +31,15 @@ async def chat(client_id: str, websocket: WebSocket): @router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201) async def init_build(graph_data: dict, flow_id: str): """Initialize the build by storing graph data and returning a unique session ID.""" - flow_data_store = service_manager.get(ServiceType.CACHE_MANAGER) + cache_manager = service_manager.get(ServiceType.CACHE_MANAGER) try: if flow_id is None: raise ValueError("No ID provided") # Check if already building if ( - flow_id in flow_data_store - and isinstance(flow_data_store[flow_id], dict) - and flow_data_store[flow_id].get("status") == BuildStatus.IN_PROGRESS + flow_id in cache_manager + and isinstance(cache_manager[flow_id], dict) + and cache_manager[flow_id].get("status") == BuildStatus.IN_PROGRESS ): return InitResponse(flowId=flow_id) @@ -48,7 +48,7 @@ async def init_build(graph_data: dict, flow_id: str): if flow_id in chat_manager.cache_manager: chat_manager.cache_manager.delete(flow_id) logger.debug(f"Deleted flow {flow_id} from cache") - flow_data_store[flow_id] = { + cache_manager[flow_id] = { "graph_data": graph_data, "status": BuildStatus.STARTED, } @@ -61,12 +61,12 @@ async def init_build(graph_data: dict, flow_id: str): @router.get("/build/{flow_id}/status", response_model=BuiltResponse) async def build_status(flow_id: str): - """Check the flow_id is in the flow_data_store.""" - flow_data_store = service_manager.get(ServiceType.CACHE_MANAGER) + """Check the flow_id is in the cache_manager.""" + cache_manager = service_manager.get(ServiceType.CACHE_MANAGER) try: built = ( - flow_id in flow_data_store - and flow_data_store[flow_id]["status"] == BuildStatus.SUCCESS + flow_id in cache_manager + and cache_manager[flow_id]["status"] == BuildStatus.SUCCESS ) return BuiltResponse( @@ -81,23 +81,23 @@ async def build_status(flow_id: str): @router.get("/build/stream/{flow_id}", response_class=StreamingResponse) async def stream_build(flow_id: str): """Stream the build process based on stored flow data.""" - flow_data_store = service_manager.get(ServiceType.CACHE_MANAGER) + cache_manager = service_manager.get(ServiceType.CACHE_MANAGER) async def event_stream(flow_id): final_response = {"end_of_stream": True} artifacts = {} try: - if flow_id not in flow_data_store: + if flow_id not in cache_manager: error_message = "Invalid session ID" yield str(StreamData(event="error", data={"error": error_message})) return - if flow_data_store[flow_id].get("status") == BuildStatus.IN_PROGRESS: + if cache_manager[flow_id].get("status") == BuildStatus.IN_PROGRESS: error_message = "Already building" yield str(StreamData(event="error", data={"error": error_message})) return - graph_data = flow_data_store[flow_id].get("graph_data") + graph_data = cache_manager[flow_id].get("graph_data") if not graph_data: error_message = "No data provided" @@ -115,7 +115,7 @@ async def stream_build(flow_id: str): return number_of_nodes = len(graph.nodes) - flow_data_store[flow_id]["status"] = BuildStatus.IN_PROGRESS + cache_manager[flow_id]["status"] = BuildStatus.IN_PROGRESS for i, vertex in enumerate(graph.generator_build(), 1): try: @@ -137,7 +137,7 @@ async def stream_build(flow_id: str): logger.exception(exc) params = str(exc) valid = False - flow_data_store[flow_id]["status"] = BuildStatus.FAILURE + cache_manager[flow_id]["status"] = BuildStatus.FAILURE response = { "valid": valid, @@ -162,14 +162,14 @@ async def stream_build(flow_id: str): } yield str(StreamData(event="message", data=input_keys_response)) chat_manager = service_manager.get(ServiceType.CHAT_MANAGER) - chat_manager.set_cache(f"{flow_id}_chat", langchain_object) + chat_manager.set_cache(flow_id, langchain_object) # We need to reset the chat history chat_manager.chat_history.empty_history(flow_id) - flow_data_store[flow_id]["status"] = BuildStatus.SUCCESS + cache_manager[flow_id]["status"] = BuildStatus.SUCCESS except Exception as exc: logger.exception(exc) logger.error("Error while building the flow: %s", exc) - flow_data_store[flow_id]["status"] = BuildStatus.FAILURE + cache_manager[flow_id]["status"] = BuildStatus.FAILURE yield str(StreamData(event="error", data={"error": str(exc)})) finally: yield str(StreamData(event="message", data=final_response)) From 20afabd90a3cc76bad2065fdad55686704637353 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 14 Aug 2023 12:23:54 -0300 Subject: [PATCH 021/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20chang?= =?UTF-8?q?e=20cache=5Fmanager.set=20to=20cache=5Fmanager.upsert=20to=20up?= =?UTF-8?q?date=20or=20insert=20cache=20value=20=E2=9C=A8=20feat(manager.p?= =?UTF-8?q?y):=20add=20result=5Fdict=20to=20cache=20instead=20of=20directl?= =?UTF-8?q?y=20setting=20langchain=5Fobject=20to=20improve=20cache=20seman?= =?UTF-8?q?tics=20=F0=9F=90=9B=20fix(manager.py):=20handle=20case=20when?= =?UTF-8?q?=20langchain=5Fobject=20is=20not=20found=20in=20cache=20and=20r?= =?UTF-8?q?aise=20ValueError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/chat/manager.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py index 6c78a2d77..b4ccd931e 100644 --- a/src/backend/langflow/chat/manager.py +++ b/src/backend/langflow/chat/manager.py @@ -170,8 +170,8 @@ class ChatManager: """ Set the cache for a client. """ - client_id = f"{client_id}_chat" - self.cache_manager.set(client_id, langchain_object) + result_dict = {"result": langchain_object, "type": type(langchain_object)} + self.cache_manager.upsert(client_id, result_dict) return client_id in self.cache_manager async def handle_websocket(self, client_id: str, websocket: WebSocket): @@ -194,9 +194,13 @@ class ChatManager: continue with self.cache_manager.set_client_id(client_id): - langchain_object = self.in_memory_cache.get(f"{client_id}_chat") - await self.process_message(client_id, payload, langchain_object) + if langchain_object := self.in_memory_cache.get(client_id).get( + "result" + ): + await self.process_message(client_id, payload, langchain_object) + else: + raise ValueError("No langchain object found in cache") except Exception as exc: # Handle any exceptions that might occur logger.error(f"Error handling websocket: {exc}") From e8d3a5df6d401e3b5add54aa71838430ca82bc71 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 14 Aug 2023 12:25:14 -0300 Subject: [PATCH 022/213] =?UTF-8?q?=F0=9F=94=A8=20chore(base.py):=20add=20?= =?UTF-8?q?upsert=20method=20to=20BaseCacheManager=20to=20provide=20a=20co?= =?UTF-8?q?mmon=20interface=20for=20inserting=20or=20updating=20cache=20it?= =?UTF-8?q?ems=20=E2=9C=A8=20feat(manager.py):=20add=20upsert=20method=20t?= =?UTF-8?q?o=20InMemoryCache=20and=20RedisCache=20to=20support=20inserting?= =?UTF-8?q?=20or=20updating=20cache=20items=20=F0=9F=90=9B=20fix(manager.p?= =?UTF-8?q?y):=20fix=20client=5Fid=20generation=20in=20handle=5Fwebsocket?= =?UTF-8?q?=20method=20to=20avoid=20overwriting=20existing=20cache=20items?= =?UTF-8?q?=20=F0=9F=94=A8=20chore(manager.py):=20refactor=20handle=5Fwebs?= =?UTF-8?q?ocket=20method=20to=20use=20upsert=20method=20and=20improve=20r?= =?UTF-8?q?eadability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/base.py | 10 +++++ .../langflow/services/cache/manager.py | 41 +++++++++++++++++++ src/backend/langflow/services/chat/manager.py | 17 ++++++-- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/services/cache/base.py b/src/backend/langflow/services/cache/base.py index 80aec1408..f40cd6663 100644 --- a/src/backend/langflow/services/cache/base.py +++ b/src/backend/langflow/services/cache/base.py @@ -32,6 +32,16 @@ class BaseCacheManager(abc.ABC, Service): value: The value to cache. """ + @abc.abstractmethod + def upsert(self, key, value): + """ + Add an item to the cache if it doesn't exist, or update it if it does. + + Args: + key: The key of the item. + value: The value to cache. + """ + @abc.abstractmethod def delete(self, key): """ diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index 6243c2507..89ef7ebcf 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -91,6 +91,27 @@ class InMemoryCache(BaseCacheManager): self._cache.popitem(last=False) self._cache[key] = {"value": value, "time": time.time()} + def upsert(self, key, value): + """ + Inserts or updates a value in the cache. + If the existing value and the new value are both dictionaries, they are merged. + + Args: + key: The key of the item. + value: The value to insert or update. + """ + with self._lock: + existing_value = self.get(key) + if ( + existing_value is not None + and isinstance(existing_value, dict) + and isinstance(value, dict) + ): + existing_value.update(value) + value = existing_value + + self.set(key, value) + def get_or_set(self, key, value): """ Retrieve an item from the cache. If the item does not exist, set it with the provided value. @@ -220,6 +241,26 @@ class RedisCache(BaseCacheManager): """ self._client.setex(key, self.expiration_time, pickle.dumps(value)) + def upsert(self, key, value): + """ + Inserts or updates a value in the cache. + If the existing value and the new value are both dictionaries, they are merged. + + Args: + key: The key of the item. + value: The value to insert or update. + """ + existing_value = self.get(key) + if ( + existing_value is not None + and isinstance(existing_value, dict) + and isinstance(value, dict) + ): + existing_value.update(value) + value = existing_value + + self.set(key, value) + def delete(self, key): """ Remove an item from the cache. diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index 76790cbb4..e4b3895c2 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -175,8 +175,11 @@ class ChatManager(Service): # client_id is the flow id but that already exists in the cache # so we need to change it to something else - client_id = f"{client_id}_chat" if "_chat" not in client_id else client_id - self.cache_manager.set(client_id, langchain_object) + result_dict = { + "result": langchain_object.result, + "type": type(langchain_object), + } + self.cache_manager.upsert(client_id, result_dict) return client_id in self.cache_manager async def handle_websocket(self, client_id: str, websocket: WebSocket): @@ -199,9 +202,15 @@ class ChatManager(Service): continue with self.chat_cache.set_client_id(client_id): - langchain_object = self.cache_manager.get(f"{client_id}_chat") - await self.process_message(client_id, payload, langchain_object) + if langchain_object := self.cache_manager.get(client_id).get( + "result" + ): + await self.process_message(client_id, payload, langchain_object) + else: + raise RuntimeError( + f"Could not find a LangChain object for client_id {client_id}" + ) except Exception as exc: # Handle any exceptions that might occur logger.error(f"Error handling websocket: {exc}") From 00b8dd7af3209a182b97fc4934348b52c6f65a4a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 14 Aug 2023 15:53:32 -0300 Subject: [PATCH 023/213] =?UTF-8?q?=F0=9F=94=A7=20chore(manager.py):=20rem?= =?UTF-8?q?ove=20unused=20import=20statement=20for=20redis=20module=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(manager.py):=20add=20try-except=20block=20?= =?UTF-8?q?to=20handle=20ImportError=20and=20provide=20clear=20error=20mes?= =?UTF-8?q?sage=20when=20redis-py=20package=20is=20not=20installed=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(manager.py):=20add=20import=20statement=20?= =?UTF-8?q?for=20redis=20module=20in=20the=20is=5Fconnected=20method=20to?= =?UTF-8?q?=20avoid=20NameError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/manager.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index 89ef7ebcf..ec9909c75 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -5,7 +5,6 @@ from collections import OrderedDict from langflow.services.cache.base import BaseCacheManager import pickle -import redis class InMemoryCache(BaseCacheManager): @@ -204,6 +203,13 @@ class RedisCache(BaseCacheManager): db (int, optional): Redis DB. expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. """ + try: + import redis + except ImportError as exc: + raise ImportError( + "RedisCache requires the redis-py package. Please install Langflow with the deploy extra: pip install langflow[deploy]" + ) from exc + self._client = redis.StrictRedis(host=host, port=port, db=db) self.expiration_time = expiration_time @@ -212,6 +218,8 @@ class RedisCache(BaseCacheManager): """ Check if the Redis client is connected. """ + import redis + try: self._client.ping() return True From a9bb04ee24594079e004cc37efe60de9d9dee1bc Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 14 Aug 2023 17:05:40 -0300 Subject: [PATCH 024/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20add=20`i?= =?UTF-8?q?s=5Ftask`=20parameter=20to=20`Vertex`=20constructor=20to=20indi?= =?UTF-8?q?cate=20if=20the=20vertex=20is=20a=20task=20=E2=9C=A8=20feat(bas?= =?UTF-8?q?e.py):=20add=20`get=5Fresult`=20method=20to=20`Vertex`=20to=20r?= =?UTF-8?q?etrieve=20the=20result=20of=20a=20built=20vertex=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(types.py):=20pass=20`is=5Ftask=3DTrue`=20to=20`super().?= =?UTF-8?q?=5F=5Finit=5F=5F`=20in=20`CustomComponentVertex`=20constructor?= =?UTF-8?q?=20=E2=9C=A8=20feat(worker.py):=20add=20`build=5Fvertex`=20task?= =?UTF-8?q?=20to=20build=20a=20vertex=20asynchronously?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/base.py | 32 ++++++++++++++++++++-- src/backend/langflow/graph/vertex/types.py | 2 +- src/backend/langflow/worker.py | 13 +++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index ac7f72b4d..39cf79bd3 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -10,13 +10,16 @@ import inspect import types from typing import Any, Dict, List, Optional from typing import TYPE_CHECKING +from celery.result import AsyncResult if TYPE_CHECKING: from langflow.graph.edge.base import Edge class Vertex: - def __init__(self, data: Dict, base_type: Optional[str] = None) -> None: + def __init__( + self, data: Dict, base_type: Optional[str] = None, is_task: bool = False + ) -> None: self.id: str = data["id"] self._data = data self.edges: List["Edge"] = [] @@ -25,6 +28,8 @@ class Vertex: self._built_object = None self._built = False self.artifacts: Dict[str, Any] = {} + self.task_id: Optional[str] = None + self.is_task = is_task def _parse_data(self) -> None: self.data = self._data["data"] @@ -168,11 +173,32 @@ class Vertex: """ return all(self._is_node(node) for node in value) + def get_result(self, timeout=None) -> Any: + # Check if the Vertex was built already + if self._built: + return self._built_object + + # Check if there's a task_id, which means it was sent to a Celery worker + if self.is_task and self.task_id is not None: + result = AsyncResult(self.task_id).get( + timeout=timeout + ) # Blocking until result is ready or timeout + if result is not None: # If result is ready + self._update_built_object_and_artifacts(result) + return self._built_object + else: + # Handle the case when the result is not ready (retry, throw exception, etc.) + pass + + # If there's no task_id, build the vertex locally + return self.build() + def _build_node_and_update_params(self, key, node): """ Builds a given node and updates the params dictionary accordingly. """ - result = node.build() + + result = node.get_result() self._handle_func(key, result) if isinstance(result, list): self._extend_params_list_with_result(key, result) @@ -184,7 +210,7 @@ class Vertex: """ self.params[key] = [] for node in nodes: - built = node.build() + built = node.get_result() if isinstance(built, list): if key not in self.params: self.params[key] = [] diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index b7ac17983..ab3e54e0b 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -247,7 +247,7 @@ class OutputParserVertex(Vertex): class CustomComponentVertex(Vertex): def __init__(self, data: Dict): - super().__init__(data, base_type="custom_components") + super().__init__(data, base_type="custom_components", is_task=True) def _built_object_repr(self): if self.artifacts and "repr" in self.artifacts: diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index d390705f1..67eaf6b3b 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -1,5 +1,9 @@ from langflow.core.celery_app import celery_app from typing import Any, Dict, Optional +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from langflow.graph.vertex.base import Vertex @celery_app.task(acks_late=True) @@ -7,6 +11,15 @@ def test_celery(word: str) -> str: return f"test task return {word}" +@celery_app.task +def build_vertex(vertex: "Vertex") -> "Vertex": + """ + Build a vertex + """ + vertex.build() + return vertex + + @celery_app.task(acks_late=True) def process_graph_cached( data_graph: Dict[str, Any], inputs: Optional[dict] = None, clear_cache=False From 861b8d048e2c6092b605d386fa88283b6352b47a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 14 Aug 2023 17:05:52 -0300 Subject: [PATCH 025/213] =?UTF-8?q?=F0=9F=94=A7=20chore(Makefile):=20updat?= =?UTF-8?q?e=20'install=5Fbackend'=20target=20to=20include=20'deploy'=20ex?= =?UTF-8?q?tras=20when=20installing=20dependencies=20with=20Poetry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ff540da0d..c31595e51 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ frontend: make run_frontend install_backend: - poetry install + poetry install --extras deploy backend: make install_backend From 6b0383be50bd0bfbdfd3bd618a200997a0d9da54 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:38:43 -0300 Subject: [PATCH 026/213] =?UTF-8?q?=F0=9F=93=A6=20chore(deploy):=20add=20d?= =?UTF-8?q?eployment=20files=20and=20configurations=20for=20Docker=20Compo?= =?UTF-8?q?se=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/backend.env | 18 ++++++++ deploy/base.Dockerfile | 87 +++++++++++++++++++++++++++++++++++++++ deploy/celeryworker.env | 8 ++++ deploy/db.env | 3 ++ deploy/docker-compose.yml | 83 +++++++++++++++++++++++++++++++++++++ deploy/flower.env | 5 +++ deploy/frontend.env | 3 ++ deploy/pgadmin.env | 2 + deploy/startup-backend.sh | 6 +++ 9 files changed, 215 insertions(+) create mode 100644 deploy/backend.env create mode 100644 deploy/base.Dockerfile create mode 100644 deploy/celeryworker.env create mode 100644 deploy/db.env create mode 100644 deploy/docker-compose.yml create mode 100644 deploy/flower.env create mode 100644 deploy/frontend.env create mode 100644 deploy/pgadmin.env create mode 100755 deploy/startup-backend.sh diff --git a/deploy/backend.env b/deploy/backend.env new file mode 100644 index 000000000..d9cbdc1dc --- /dev/null +++ b/deploy/backend.env @@ -0,0 +1,18 @@ +# Database configuration +DB_USER=langflow +DB_PASSWORD=langflow +DB_HOST=db +DB_PORT=5432 +DB_NAME=langflow + +# Logging configuration +LOG_LEVEL=debug + +# Cache configuration +LANGFLOW_CACHE_TYPE=redis + +# Redis configuration +LANGFLOW_REDIS_HOST=queue +LANGFLOW_REDIS_PORT=6379 +LANGFLOW_REDIS_DB=0 +LANGFLOW_REDIS_EXPIRE=3600 \ No newline at end of file diff --git a/deploy/base.Dockerfile b/deploy/base.Dockerfile new file mode 100644 index 000000000..1be280163 --- /dev/null +++ b/deploy/base.Dockerfile @@ -0,0 +1,87 @@ + + +# syntax=docker/dockerfile:1 +# Keep this syntax directive! It's used to enable Docker BuildKit + +# Based on https://github.com/python-poetry/poetry/discussions/1879?sort=top#discussioncomment-216865 +# but I try to keep it updated (see history) + +################################ +# PYTHON-BASE +# Sets up all our shared environment variables +################################ +FROM python:3.10-slim as python-base + +# python +ENV PYTHONUNBUFFERED=1 \ + # prevents python creating .pyc files + PYTHONDONTWRITEBYTECODE=1 \ + \ + # pip + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 \ + \ + # poetry + # https://python-poetry.org/docs/configuration/#using-environment-variables + POETRY_VERSION=1.5.1 \ + # make poetry install to this location + POETRY_HOME="/opt/poetry" \ + # make poetry create the virtual environment in the project's root + # it gets named `.venv` + POETRY_VIRTUALENVS_IN_PROJECT=true \ + # do not ask any interactive question + POETRY_NO_INTERACTION=1 \ + \ + # paths + # this is where our requirements + virtual environment will live + PYSETUP_PATH="/opt/pysetup" \ + VENV_PATH="/opt/pysetup/.venv" + + +# prepend poetry and venv to path +ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" + + +################################ +# BUILDER-BASE +# Used to build deps + create our virtual environment +################################ +FROM python-base as builder-base +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + # deps for installing poetry + curl \ + # deps for building python deps + build-essential + +# install poetry - respects $POETRY_VERSION & $POETRY_HOME +# The --mount will mount the buildx cache directory to where +# Poetry and Pip store their cache so that they can re-use it +RUN --mount=type=cache,target=/root/.cache \ + curl -sSL https://install.python-poetry.org | python3 - + +# copy project requirement files here to ensure they will be cached. +WORKDIR $PYSETUP_PATH +COPY ./poetry.lock ./pyproject.toml ./ +COPY ./src/backend ./src/backend +# Copy README.md to the build context +COPY ./README.md ./ +# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally +RUN --mount=type=cache,target=/root/.cache \ + poetry install --without dev --extras deploy + + +################################ +# DEVELOPMENT +# Image used during development / testing +################################ +FROM python-base as development +WORKDIR $PYSETUP_PATH + +# copy in our built poetry + venv +COPY --from=builder-base $POETRY_HOME $POETRY_HOME +COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH + +# quicker install as runtime deps are already installed +RUN --mount=type=cache,target=/root/.cache \ + poetry install --with=dev --extras deploy diff --git a/deploy/celeryworker.env b/deploy/celeryworker.env new file mode 100644 index 000000000..bbc6aa8c1 --- /dev/null +++ b/deploy/celeryworker.env @@ -0,0 +1,8 @@ +LANGFLOW_CACHE_TYPE=redis +LANGFLOW_REDIS_HOST=queue +LANGFLOW_REDIS_PORT=6379 +LANGFLOW_REDIS_DB=0 +LANGFLOW_REDIS_EXPIRE=3600 +BROKER_URL=redis://queue:6379/0 +RESULT_BACKEND=redis://queue:6379/0 +C_FORCE_ROOT="true # ! Only for development" diff --git a/deploy/db.env b/deploy/db.env new file mode 100644 index 000000000..64e9ce762 --- /dev/null +++ b/deploy/db.env @@ -0,0 +1,3 @@ +POSTGRES_USER=langflow +POSTGRES_PASSWORD=langflow +POSTGRES_DB=langflow diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 000000000..293115182 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,83 @@ +version: "3" + +services: + backend: + build: + context: ../ + dockerfile: base.Dockerfile + # user: your-non-root-user # Make sure your Dockerfile creates this user + depends_on: + - queue + - db + env_file: + - ./backend.env + ports: + - "7860:7860" + volumes: + - ./:/app + - ./startup-backend.sh:/startup-backend.sh # Ensure the paths match + command: /startup-backend.sh # Fixed the path + + db: + image: postgres:15.4 + env_file: + - ./db.env + ports: + - "5432:5432" + + pgadmin: + image: dpage/pgadmin4 + env_file: + - ./pgadmin.env + ports: + - "5050:80" + depends_on: + - db + volumes: + - ./pgadmin:/var/lib/pgadmin + + queue: + image: redis:6.2.5 # Use a specific version + ports: + - "6379:6379" + + celeryworker: + # user: your-non-root-user + depends_on: + - queue + env_file: + - ./celeryworker.env + build: + context: ../ + dockerfile: base.Dockerfile + command: celery -A langflow.worker.celery_app worker --loglevel=INFO + + flower: + # user: your-non-root-user + networks: + - default + depends_on: + - queue + build: + context: ../ + dockerfile: base.Dockerfile + env_file: + - ./flower.env + command: celery -A langflow.worker.celery_app --broker=redis://queue:6379/0 flower --port=5555 + ports: + - "5555:5555" + + frontend: + # user: your-non-root-user + build: + context: ../src/frontend + dockerfile: Dockerfile + args: + - BACKEND_URL=http://backend:7860 + depends_on: + - backend + env_file: + - ./frontend.env + ports: + - "80:80" + restart: on-failure diff --git a/deploy/flower.env b/deploy/flower.env new file mode 100644 index 000000000..df742505f --- /dev/null +++ b/deploy/flower.env @@ -0,0 +1,5 @@ +LANGFLOW_CACHE_TYPE=redis +LANGFLOW_REDIS_HOST=queue +LANGFLOW_REDIS_PORT=6379 +LANGFLOW_REDIS_DB=0 +LANGFLOW_REDIS_EXPIRE=3600 diff --git a/deploy/frontend.env b/deploy/frontend.env new file mode 100644 index 000000000..ca56a25a7 --- /dev/null +++ b/deploy/frontend.env @@ -0,0 +1,3 @@ + +VITE_PROXY_TARGET=http://backend:7860/api/ +BACKEND_URL=http://backend:7860 diff --git a/deploy/pgadmin.env b/deploy/pgadmin.env new file mode 100644 index 000000000..7ef01290b --- /dev/null +++ b/deploy/pgadmin.env @@ -0,0 +1,2 @@ +PGADMIN_DEFAULT_EMAIL=admin@admin.com +PGADMIN_DEFAULT_PASSWORD=admin diff --git a/deploy/startup-backend.sh b/deploy/startup-backend.sh new file mode 100755 index 000000000..cf63444b5 --- /dev/null +++ b/deploy/startup-backend.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export LANGFLOW_DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}" + +# Your command to start the backend +exec uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level ${LOG_LEVEL:-info} From 284158b33b2ce848745fe6bd28e1887cf58aee10 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:39:08 -0300 Subject: [PATCH 027/213] =?UTF-8?q?=F0=9F=93=A6=20chore(deploy=5Flangflow?= =?UTF-8?q?=5Fgcp.sh):=20add=20script=20to=20deploy=20Langflow=20on=20Goog?= =?UTF-8?q?le=20Cloud=20Platform?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📝 docs(walkthroughtutorial.md): add tutorial for deploying Langflow on Google Cloud Platform The `deploy_langflow_gcp.sh` script is added to the `scripts/gcp` directory. This script sets up the necessary VM, image, and networking configuration for deploying Langflow on Google Cloud Platform. It also creates firewall rules, installs dependencies, and starts the Langflow server. The `walkthroughtutorial.md` file is added to the `scripts/gcp` directory. This file provides a step-by-step tutorial on how to deploy Langflow on Google Cloud Platform using the `deploy_langflow_gcp.sh` script. It includes instructions on setting up the GCP environment, running the script, and connecting to the Langflow VM. Cleanup instructions are also provided in the tutorial for removing the resources created during the deployment process. 📝 docs(walkthroughtutorial_spot.md): add walkthrough tutorial for deploying Langflow on Google Cloud Platform This commit adds a new file `walkthroughtutorial_spot.md` which contains a step-by-step tutorial for deploying Langflow on Google Cloud Platform (GCP) using Google Cloud Shell. The tutorial provides an introduction, prerequisites, instructions for setting up the GCP environment, deploying Langflow, connecting to the Langflow VM, and cleaning up resources after the deployment. The tutorial is authored by Robert Wilkins III and has a duration of 45 minutes. It assumes that the user has a GCP account and basic knowledge of Google Cloud Shell. The tutorial also includes links to relevant documentation and provides commands for configuring the GCP environment and deploying Langflow. The tutorial is comprehensive and aims to guide users through the process of deploying Langflow on GCP. It also includes cleanup instructions for removing the resources created during the tutorial if desired. --- scripts/{ => gcp}/deploy_langflow_gcp.sh | 0 scripts/{ => gcp}/deploy_langflow_gcp_spot.sh | 0 scripts/{ => gcp}/walkthroughtutorial.md | 0 scripts/{ => gcp}/walkthroughtutorial_spot.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename scripts/{ => gcp}/deploy_langflow_gcp.sh (100%) rename scripts/{ => gcp}/deploy_langflow_gcp_spot.sh (100%) rename scripts/{ => gcp}/walkthroughtutorial.md (100%) rename scripts/{ => gcp}/walkthroughtutorial_spot.md (100%) diff --git a/scripts/deploy_langflow_gcp.sh b/scripts/gcp/deploy_langflow_gcp.sh similarity index 100% rename from scripts/deploy_langflow_gcp.sh rename to scripts/gcp/deploy_langflow_gcp.sh diff --git a/scripts/deploy_langflow_gcp_spot.sh b/scripts/gcp/deploy_langflow_gcp_spot.sh similarity index 100% rename from scripts/deploy_langflow_gcp_spot.sh rename to scripts/gcp/deploy_langflow_gcp_spot.sh diff --git a/scripts/walkthroughtutorial.md b/scripts/gcp/walkthroughtutorial.md similarity index 100% rename from scripts/walkthroughtutorial.md rename to scripts/gcp/walkthroughtutorial.md diff --git a/scripts/walkthroughtutorial_spot.md b/scripts/gcp/walkthroughtutorial_spot.md similarity index 100% rename from scripts/walkthroughtutorial_spot.md rename to scripts/gcp/walkthroughtutorial_spot.md From 49729263ef958a90f9377684d00abcd12fc2a062 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:40:14 -0300 Subject: [PATCH 028/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20fix?= =?UTF-8?q?=20incorrect=20assignment=20of=20langchain=5Fobject.result=20to?= =?UTF-8?q?=20result=5Fdict["result"]=20to=20store=20the=20entire=20langch?= =?UTF-8?q?ain=5Fobject=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/chat/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index e4b3895c2..394455b22 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -176,7 +176,7 @@ class ChatManager(Service): # so we need to change it to something else result_dict = { - "result": langchain_object.result, + "result": langchain_object, "type": type(langchain_object), } self.cache_manager.upsert(client_id, result_dict) From c2ec95fbb7d54a58463d2a9aa920d7a86b937baa Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:40:54 -0300 Subject: [PATCH 029/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.Dockerfile):=20?= =?UTF-8?q?change=20`--with=20deploy`=20to=20`--extras=20deploy`=20in=20po?= =?UTF-8?q?etry=20install=20command=20to=20correctly=20install=20deploy=20?= =?UTF-8?q?extras?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base.Dockerfile b/base.Dockerfile index 2306f7bf6..632a0fb46 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -68,7 +68,7 @@ COPY ./src/backend ./src/backend COPY README.md . # install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally RUN --mount=type=cache,target=/root/.cache \ - poetry install --without dev --with deploy + poetry install --without dev --extras deploy ################################ @@ -84,4 +84,4 @@ COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH # quicker install as runtime deps are already installed RUN --mount=type=cache,target=/root/.cache \ - poetry install --with=dev + poetry install --with=dev --extras deploy From 40321f4be75992b7bf21018104b761cfcf6889df Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:41:09 -0300 Subject: [PATCH 030/213] =?UTF-8?q?=F0=9F=90=9B=20fix(frontend):=20update?= =?UTF-8?q?=20Dockerfile=20to=20use=20node:20-alpine=20as=20base=20image?= =?UTF-8?q?=20for=20frontend=20build=20=E2=9C=A8=20feat(frontend):=20add?= =?UTF-8?q?=20support=20for=20BACKEND=5FURL=20environment=20variable=20in?= =?UTF-8?q?=20nginx.conf=20to=20configure=20backend=20URL=20=F0=9F=93=9D?= =?UTF-8?q?=20chore(frontend):=20add=20start-nginx.sh=20script=20to=20repl?= =?UTF-8?q?ace=20placeholder=20in=20nginx.conf=20with=20actual=20BACKEND?= =?UTF-8?q?=5FURL=20and=20start=20nginx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/Dockerfile | 14 ++++++++---- src/frontend/nginx.conf | 45 +++++++++++++++++++++++++------------ src/frontend/start-nginx.sh | 8 +++++++ 3 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 src/frontend/start-nginx.sh diff --git a/src/frontend/Dockerfile b/src/frontend/Dockerfile index 010811e7b..81abf18cd 100644 --- a/src/frontend/Dockerfile +++ b/src/frontend/Dockerfile @@ -1,10 +1,16 @@ -FROM node:14-alpine as frontend_build -ARG BACKEND +FROM node:20-alpine as frontend_build +ARG BACKEND_URL WORKDIR /app -COPY . /app +COPY ./src /app/src +COPY ./package.json ./package-lock.json ./tsconfig.json ./vite.config.ts ./index.html ./tailwind.config.js ./postcss.config.js ./prettier.config.js /app/ + RUN npm install RUN npm run build FROM nginx COPY --from=frontend_build /app/build/ /usr/share/nginx/html -COPY /nginx.conf /etc/nginx/conf.d/default.conf \ No newline at end of file +COPY /nginx.conf /etc/nginx/conf.d/default.conf +COPY start-nginx.sh /start-nginx.sh +RUN chmod +x /start-nginx.sh +ENV BACKEND_URL=$BACKEND_URL +CMD ["/start-nginx.sh"] \ No newline at end of file diff --git a/src/frontend/nginx.conf b/src/frontend/nginx.conf index df665f5b6..d069a206d 100644 --- a/src/frontend/nginx.conf +++ b/src/frontend/nginx.conf @@ -1,18 +1,35 @@ server { - gzip on; - gzip_comp_level 2; - gzip_min_length 1000; - gzip_types text/xml text/css; - gzip_http_version 1.1; - gzip_vary on; - gzip_disable "MSIE [4-6] \."; + gzip on; + gzip_comp_level 2; + gzip_min_length 1000; + gzip_types text/xml text/css; + gzip_http_version 1.1; + gzip_vary on; + gzip_disable "MSIE [4-6] \."; - listen 80; + listen 80; - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html =404; - } - + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } + + location ~ ^/api/v1/ { # Matching the /api/v1/ route + proxy_pass __BACKEND_URL__; # URL of your backend service + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /health { # Matching the /health route + proxy_pass __BACKEND_URL__; # URL of your backend service + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } } diff --git a/src/frontend/start-nginx.sh b/src/frontend/start-nginx.sh new file mode 100644 index 000000000..dd20f5305 --- /dev/null +++ b/src/frontend/start-nginx.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Replace the placeholder with the actual value +sed -i "s|__BACKEND_URL__|$BACKEND_URL|g" /etc/nginx/conf.d/default.conf + + +# Start nginx +exec nginx -g 'daemon off;' From e1872b821320007f99d8dff8d836d09ab40a48a9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:41:55 -0300 Subject: [PATCH 031/213] =?UTF-8?q?=F0=9F=94=92=20chore(poetry.lock):=20up?= =?UTF-8?q?date=20cohere=20package=20version=20to=204.20.1=20=F0=9F=94=92?= =?UTF-8?q?=20chore(poetry.lock):=20update=20exceptiongroup=20package=20ve?= =?UTF-8?q?rsion=20to=201.1.3=20=F0=9F=94=92=20chore(poetry.lock):=20updat?= =?UTF-8?q?e=20google-api-python-client=20package=20version=20to=202.97.0?= =?UTF-8?q?=20=F0=9F=94=92=20chore(poetry.lock):=20update=20pandas-stubs?= =?UTF-8?q?=20package=20version=20to=202.0.3.230814=20=F0=9F=94=92=20chore?= =?UTF-8?q?(poetry.lock):=20update=20setuptools=20package=20version=20to?= =?UTF-8?q?=2068.1.0=20=F0=9F=94=92=20chore(poetry.lock):=20update=20tenac?= =?UTF-8?q?ity=20package=20version=20to=208.2.3=20=F0=9F=94=92=20chore(poe?= =?UTF-8?q?try.lock):=20update=20textual=20package=20version=20to=200.33.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/poetry.lock b/poetry.lock index f38089432..cf9357228 100644 --- a/poetry.lock +++ b/poetry.lock @@ -888,13 +888,13 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"] [[package]] name = "cohere" -version = "4.19.3" +version = "4.20.1" description = "" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "cohere-4.19.3-py3-none-any.whl", hash = "sha256:6c98f1e58b93b6316c824385c1d2032ed352280e9efa5695ba98306258abf84f"}, - {file = "cohere-4.19.3.tar.gz", hash = "sha256:c3aaa716c4da7d7a8ed68705fcdc92f1b1a2260b737cee6bd27af5c347f31496"}, + {file = "cohere-4.20.1-py3-none-any.whl", hash = "sha256:4466c7abdbb168fe2893e5b5123882fe91691d0e3fe43ee254b411917f780dfc"}, + {file = "cohere-4.20.1.tar.gz", hash = "sha256:533e4a45b38dc338f8a27f24e098b652b4f5ed9f9fbb4719780d1a9fcd1af39e"}, ] [package.dependencies] @@ -1397,13 +1397,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.2" +version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, - {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] @@ -1744,13 +1744,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.96.0" +version = "2.97.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-python-client-2.96.0.tar.gz", hash = "sha256:f712373d03d338af57b9f5fe98c91f4b5baaa8765469b015bc623c4681c5bd51"}, - {file = "google_api_python_client-2.96.0-py2.py3-none-any.whl", hash = "sha256:38c2b61b10d15bb41ec8f89303e3837ec2d2c3e4e38de5800c05ee322492f937"}, + {file = "google-api-python-client-2.97.0.tar.gz", hash = "sha256:48277291894876a1ca7ed4127e055e81f81e6343ced1b544a7200ae2c119dcd7"}, + {file = "google_api_python_client-2.97.0-py2.py3-none-any.whl", hash = "sha256:5215f4cd577753fc4192ccfbe0bb8b55d4bb5fd68fa6268ac5cf271b6305de31"}, ] [package.dependencies] @@ -4423,17 +4423,17 @@ xml = ["lxml (>=4.6.3)"] [[package]] name = "pandas-stubs" -version = "2.0.2.230605" +version = "2.0.3.230814" description = "Type annotations for pandas" optional = false python-versions = ">=3.8" files = [ - {file = "pandas_stubs-2.0.2.230605-py3-none-any.whl", hash = "sha256:39106b602f3cb6dc5f728b84e1b32bde6ecf41ee34ee714c66228009609fbada"}, - {file = "pandas_stubs-2.0.2.230605.tar.gz", hash = "sha256:624c7bb06d38145a44b61be459ccd19b038e0bf20364a025ecaab78fea65e858"}, + {file = "pandas_stubs-2.0.3.230814-py3-none-any.whl", hash = "sha256:4b3dfc027d49779176b7daa031a3405f7b839bcb6e312f4b9f29fea5feec5b4f"}, + {file = "pandas_stubs-2.0.3.230814.tar.gz", hash = "sha256:1d5cc09e36e3d9f9a1ed9dceae4e03eeb26d1b898dd769996925f784365c8769"}, ] [package.dependencies] -numpy = ">=1.24.3" +numpy = {version = ">=1.25.0", markers = "python_version >= \"3.9\""} types-pytz = ">=2022.1.1" [[package]] @@ -6262,18 +6262,18 @@ files = [ [[package]] name = "setuptools" -version = "68.0.0" +version = "68.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "setuptools-68.1.0-py3-none-any.whl", hash = "sha256:e13e1b0bc760e9b0127eda042845999b2f913e12437046e663b833aa96d89715"}, + {file = "setuptools-68.1.0.tar.gz", hash = "sha256:d59c97e7b774979a5ccb96388efc9eb65518004537e85d52e81eaee89ab6dd91"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -6649,13 +6649,13 @@ widechars = ["wcwidth"] [[package]] name = "tenacity" -version = "8.2.2" +version = "8.2.3" description = "Retry code until it succeeds" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "tenacity-8.2.2-py3-none-any.whl", hash = "sha256:2f277afb21b851637e8f52e6a613ff08734c347dc19ade928e519d7d2d8569b0"}, - {file = "tenacity-8.2.2.tar.gz", hash = "sha256:43af037822bd0029025877f3b2d97cc4d7bb0c2991000a3d59d71517c5c969e0"}, + {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, + {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, ] [package.extras] @@ -6663,13 +6663,13 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"] [[package]] name = "textual" -version = "0.32.0" +version = "0.33.0" description = "Modern Text User Interface framework" optional = true python-versions = ">=3.7,<4.0" files = [ - {file = "textual-0.32.0-py3-none-any.whl", hash = "sha256:81fc68406c8806bc864e2f035874a868b4ff0cf466289dce5f7b31869949383b"}, - {file = "textual-0.32.0.tar.gz", hash = "sha256:f7b6683bc18faee6fd3c47cfbad43fbf8273c5fecc12230d52ce5ee089021327"}, + {file = "textual-0.33.0-py3-none-any.whl", hash = "sha256:698a093add0fd21c786232bcacde9ff427a9d5dc9ea5deca93437d9453e4ead1"}, + {file = "textual-0.33.0.tar.gz", hash = "sha256:e0a98b1d9c4458c5bb4269c65d0a7f3e926f197411242d2f8faf80183d47a728"}, ] [package.dependencies] From c93390c0e9545b89413e4e8d4e7fb6278ec9f467 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 15:42:10 -0300 Subject: [PATCH 032/213] =?UTF-8?q?=F0=9F=90=B3=20chore(docker-compose.cel?= =?UTF-8?q?ery.yml):=20add=20PostgreSQL=20and=20pgAdmin=20services=20to=20?= =?UTF-8?q?support=20database=20operations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ feat(docker-compose.celery.yml): add db service with PostgreSQL image and configure environment variables for database connection ✨ feat(docker-compose.celery.yml): add pgadmin service with pgAdmin4 image and configure environment variables for admin login 🔧 chore(docker-compose.celery.yml): add db service as a dependency for the queue service --- docker-compose.celery.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docker-compose.celery.yml b/docker-compose.celery.yml index a568be593..88e3cc189 100644 --- a/docker-compose.celery.yml +++ b/docker-compose.celery.yml @@ -7,18 +7,41 @@ services: dockerfile: base.Dockerfile depends_on: - queue + - db environment: - LANGFLOW_CACHE_TYPE=redis - LANGFLOW_REDIS_HOST=queue - LANGFLOW_REDIS_PORT=6379 - LANGFLOW_REDIS_DB=0 - LANGFLOW_REDIS_EXPIRE=3600 + - LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@db:5432/langflow ports: - "7860:7860" volumes: - ./:/app command: bash -c "uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level debug" + db: + image: postgres:15.4 + environment: + - POSTGRES_USER=langflow + - POSTGRES_PASSWORD=langflow + - POSTGRES_DB=langflow + ports: + - "5432:5432" + + pgadmin: + image: dpage/pgadmin4 + environment: + PGADMIN_DEFAULT_EMAIL: admin@admin.com + PGADMIN_DEFAULT_PASSWORD: admin + ports: + - "5050:80" + depends_on: + - db + volumes: + - pgadmin:/var/lib/pgadmin + queue: image: redis:latest ports: From c2cfcafc175aed6cdbb08c718e96506d1a5b6779 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 19:20:24 -0300 Subject: [PATCH 033/213] =?UTF-8?q?=F0=9F=90=9B=20fix(Dockerfile):=20chang?= =?UTF-8?q?e=20order=20of=20COPY=20commands=20to=20improve=20build=20cachi?= =?UTF-8?q?ng=20and=20efficiency=20=F0=9F=90=9B=20fix(exportModal/index.ts?= =?UTF-8?q?x):=20add=20state=20for=20invalidName=20to=20handle=20validatio?= =?UTF-8?q?n=20of=20name=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/Dockerfile | 4 ++-- src/frontend/src/modals/exportModal/index.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/Dockerfile b/src/frontend/Dockerfile index 81abf18cd..024eab562 100644 --- a/src/frontend/Dockerfile +++ b/src/frontend/Dockerfile @@ -1,10 +1,10 @@ FROM node:20-alpine as frontend_build ARG BACKEND_URL WORKDIR /app -COPY ./src /app/src -COPY ./package.json ./package-lock.json ./tsconfig.json ./vite.config.ts ./index.html ./tailwind.config.js ./postcss.config.js ./prettier.config.js /app/ +COPY ./package.json ./package-lock.json ./tsconfig.json ./vite.config.ts ./index.html ./tailwind.config.js ./postcss.config.js ./prettier.config.js /app/ RUN npm install +COPY ./src /app/src RUN npm run build FROM nginx diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index 7514f955e..160ab599f 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -19,6 +19,7 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => { const [name, setName] = useState(flow.name); const [description, setDescription] = useState(flow.description); const [open, setOpen] = useState(false); + const [invalidName, setInvalidName] = useState(false); return ( From be4f949e704fd6a3e97af17c2cdd0ce5541133d9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 19:27:26 -0300 Subject: [PATCH 034/213] =?UTF-8?q?=F0=9F=93=A6=20chore(deploy):=20add=20.?= =?UTF-8?q?gitignore=20file=20to=20ignore=20'pgadmin'=20directory=20in=20t?= =?UTF-8?q?he=20deploy=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 deploy/.gitignore diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 000000000..fdbcefec6 --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1 @@ +pgadmin \ No newline at end of file From ce8fc2739dd9e395ad81001c6b7f9878a00faf06 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 22:36:34 -0300 Subject: [PATCH 035/213] =?UTF-8?q?=F0=9F=94=A7=20chore(run.py):=20replace?= =?UTF-8?q?=20deprecated=20memoize=5Fdict=20decorator=20with=20Memoize=20c?= =?UTF-8?q?lass=20to=20improve=20code=20maintainability=20and=20readabilit?= =?UTF-8?q?y=20=F0=9F=94=A7=20chore(process.py):=20replace=20deprecated=20?= =?UTF-8?q?build=5Fsorted=5Fvertices=5Fwith=5Fcaching.hash=20with=20build?= =?UTF-8?q?=5Fsorted=5Fvertices=5Fwith=5Fcaching.session=5Fid=20to=20fix?= =?UTF-8?q?=20incorrect=20session=5Fid=20assignment=20=F0=9F=94=A7=20chore?= =?UTF-8?q?(utils.py):=20add=20get=5Fcache=5Fmanager=20function=20to=20ret?= =?UTF-8?q?rieve=20the=20cache=20manager=20from=20the=20service=20manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/run.py | 7 ++-- src/backend/langflow/processing/process.py | 2 +- src/backend/langflow/services/cache/utils.py | 41 +++++++++++++++++++- src/backend/langflow/services/utils.py | 4 ++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 42cea0e98..7b7a16b69 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -1,10 +1,11 @@ from typing import Any, Dict, Tuple -from langflow.services.cache.utils import memoize_dict +from langflow.services.cache.utils import Memoize from langflow.graph import Graph from langflow.utils.logger import logger +from langflow.services.utils import get_cache_manager -@memoize_dict(maxsize=10) +@Memoize(get_cache_manager=get_cache_manager) def build_langchain_object_with_caching(data_graph): """ Build langchain object from data_graph. @@ -15,7 +16,7 @@ def build_langchain_object_with_caching(data_graph): return graph.build() -@memoize_dict(maxsize=10) +@Memoize(get_cache_manager=get_cache_manager) def build_sorted_vertices_with_caching(data_graph) -> Tuple[Any, Dict]: """ Build langchain object from data_graph. diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 396135e16..71904f79f 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -111,7 +111,7 @@ def load_langchain_object( data_graph: Dict[str, Any], session_id: str ) -> Tuple[Union[Chain, VectorStore], Dict[str, Any], str]: langchain_object, artifacts = get_build_result(data_graph, session_id) - session_id = build_sorted_vertices_with_caching.hash + session_id = build_sorted_vertices_with_caching.session_id logger.debug("Loaded LangChain object") if langchain_object is None: diff --git a/src/backend/langflow/services/cache/utils.py b/src/backend/langflow/services/cache/utils.py index 2333eb5f4..13e24dd6d 100644 --- a/src/backend/langflow/services/cache/utils.py +++ b/src/backend/langflow/services/cache/utils.py @@ -7,9 +7,12 @@ import os import tempfile from collections import OrderedDict from pathlib import Path -from typing import Any, Dict +from typing import TYPE_CHECKING, Any, Callable, Dict from appdirs import user_cache_dir +if TYPE_CHECKING: + from langflow.services.cache.base import BaseCacheManager + CACHE: Dict[str, Any] = {} CACHE_DIR = user_cache_dir("langflow", "langflow") @@ -191,3 +194,39 @@ def save_uploaded_file(file, folder_name): new_file.write(chunk) return file_path + + +class Memoize: + def __init__( + self, + get_cache_manager: Callable[[], "BaseCacheManager"], + ): + self.get_cache_manager = get_cache_manager + self.hash_func = compute_dict_hash + + def clear_cache(self, session_id): + cache_manager = self.get_cache_manager() + cache_manager.delete(session_id) + + def get_result_by_session_id(self, session_id): + cache_manager = self.get_cache_manager() + return cache_manager.get(session_id) + + def __call__(self, func: Callable[..., Any]): + @functools.wraps(func) + def wrapper(*args, **kwargs): + cache_manager = self.get_cache_manager() + session_id = self.hash_func(args[0]) + + result = cache_manager.get(session_id) + + if result is None: + result = func(*args, **kwargs) + cache_manager.set(session_id, result) + + wrapper.session_id = session_id + return result + + wrapper.clear_cache = self.clear_cache + wrapper.get_result_by_session_id = self.get_result_by_session_id + return wrapper diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index 049e82c0f..1c5796fb4 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -16,3 +16,7 @@ def get_db_manager(): def get_session(): db_manager = service_manager.get(ServiceType.DATABASE_MANAGER) yield from db_manager.get_session() + + +def get_cache_manager(): + return service_manager.get(ServiceType.CACHE_MANAGER) From d39dd5fc4a753c6e076bf4e3f0088ac9ba371916 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 22:38:18 -0300 Subject: [PATCH 036/213] =?UTF-8?q?=F0=9F=90=9B=20fix(test=5Fcache.py):=20?= =?UTF-8?q?import=20correct=20compute=5Fdict=5Fhash=20function=20from=20la?= =?UTF-8?q?ngflow.services.cache.utils=20module=20=F0=9F=90=9B=20fix(test?= =?UTF-8?q?=5Fcache.py):=20fix=20clear=5Fcache=20function=20call=20by=20pa?= =?UTF-8?q?ssing=20session=5Fid=20parameter=20=F0=9F=90=9B=20fix(test=5Fca?= =?UTF-8?q?che=5Fmanager.py):=20import=20CacheManager=20from=20langflow.se?= =?UTF-8?q?rvices.chat.cache=20module=20instead=20of=20langflow.services.c?= =?UTF-8?q?ache.manager=20=F0=9F=90=9B=20fix(test=5Fcli.py):=20convert=20t?= =?UTF-8?q?emp=5Fdir=20to=20string=20before=20checking=20if=20it=20is=20in?= =?UTF-8?q?=20COMPONENTS=5FPATH=20=F0=9F=90=9B=20fix(test=5Fprocess.py):?= =?UTF-8?q?=20fix=20clear=5Fcache=20function=20call=20by=20passing=20sessi?= =?UTF-8?q?on=5Fid=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_cache.py | 22 ++++------------------ tests/test_cache_manager.py | 2 +- tests/test_cli.py | 2 +- tests/test_process.py | 2 +- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/tests/test_cache.py b/tests/test_cache.py index 50698c304..94e4e8553 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -1,5 +1,6 @@ import json from langflow.graph import Graph +from langflow.services.cache.utils import compute_dict_hash import pytest from langflow.interface.run import ( @@ -41,8 +42,9 @@ def langchain_objects_are_equal(obj1, obj2): # Test build_langchain_object_with_caching -def test_build_langchain_object_with_caching(basic_data_graph): - build_langchain_object_with_caching.clear_cache() +def test_build_langchain_object_with_caching(client, basic_data_graph): + session_id = compute_dict_hash(basic_data_graph) + build_langchain_object_with_caching.clear_cache(session_id) graph = build_langchain_object_with_caching(basic_data_graph) assert graph is not None @@ -53,19 +55,3 @@ def test_build_graph(basic_data_graph): assert graph is not None assert len(graph.nodes) == len(basic_data_graph["nodes"]) assert len(graph.edges) == len(basic_data_graph["edges"]) - - -# Test cache size limit -def test_cache_size_limit(basic_data_graph): - build_langchain_object_with_caching.clear_cache() - for i in range(11): - modified_data_graph = basic_data_graph.copy() - nodes = modified_data_graph["nodes"] - node_id = nodes[0]["id"] - # Now we replace all instances ode node_id with a new id in the json - json_string = json.dumps(modified_data_graph) - modified_json_string = json_string.replace(node_id, f"{node_id}_{i}") - modified_data_graph_new_id = json.loads(modified_json_string) - build_langchain_object_with_caching(modified_data_graph_new_id) - - assert len(build_langchain_object_with_caching.cache) == 10 diff --git a/tests/test_cache_manager.py b/tests/test_cache_manager.py index 660512634..a560aff88 100644 --- a/tests/test_cache_manager.py +++ b/tests/test_cache_manager.py @@ -2,7 +2,7 @@ from io import StringIO import pandas as pd import pytest -from langflow.services.cache.manager import CacheManager +from langflow.services.chat.cache import CacheManager from PIL import Image diff --git a/tests/test_cli.py b/tests/test_cli.py index 408500d7a..c990ef9e8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -27,4 +27,4 @@ def test_components_path(runner, client, default_settings): ) assert result.exit_code == 0, result.stdout settings_manager = utils.get_settings_manager() - assert temp_dir in settings_manager.settings.COMPONENTS_PATH + assert str(temp_dir) in settings_manager.settings.COMPONENTS_PATH diff --git a/tests/test_process.py b/tests/test_process.py index a0d91b5df..29c125abf 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -218,7 +218,7 @@ def test_load_langchain_object_with_no_cached_session(client, basic_graph_data): basic_graph_data, "non_existent_session" ) # Clear the cache - build_sorted_vertices_with_caching.clear_cache() + build_sorted_vertices_with_caching.clear_cache(session_id1) # Use the new session_id to get the langchain_object again langchain_object2, artifacts2, session_id2 = load_langchain_object( basic_graph_data, session_id1 From cf96523dc3dd7a2d0e315e5e1adda11a3a7facc8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 16 Aug 2023 22:50:34 -0300 Subject: [PATCH 037/213] =?UTF-8?q?=F0=9F=93=A6=20feat(pyproject.toml):=20?= =?UTF-8?q?add=20locust=20package=20as=20a=20dependency=20to=20enable=20lo?= =?UTF-8?q?ad=20testing=20capabilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 531 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 530 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4fecda219..5e9e116ae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -476,6 +476,108 @@ webencodings = "*" [package.extras] css = ["tinycss2 (>=1.1.0,<1.2)"] +[[package]] +name = "blinker" +version = "1.6.2" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.7" +files = [ + {file = "blinker-1.6.2-py3-none-any.whl", hash = "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0"}, + {file = "blinker-1.6.2.tar.gz", hash = "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213"}, +] + +[[package]] +name = "brotli" +version = "1.0.9" +description = "Python bindings for the Brotli compression library" +optional = false +python-versions = "*" +files = [ + {file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"}, + {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"}, + {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"}, + {file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"}, + {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"}, + {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"}, + {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"}, + {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"}, + {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"}, + {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"}, + {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"}, + {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"}, + {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"}, + {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"}, + {file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"}, + {file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"}, + {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a"}, + {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df"}, + {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad"}, + {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde"}, + {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a"}, + {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f"}, + {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d"}, + {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f"}, + {file = "Brotli-1.0.9-cp311-cp311-win32.whl", hash = "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d"}, + {file = "Brotli-1.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679"}, + {file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"}, + {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"}, + {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"}, + {file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"}, + {file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"}, + {file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"}, + {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"}, + {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"}, + {file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"}, + {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"}, + {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"}, + {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"}, + {file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"}, + {file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"}, + {file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"}, + {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"}, + {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"}, + {file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"}, + {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"}, + {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"}, + {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"}, + {file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"}, + {file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"}, + {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"}, + {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"}, + {file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"}, + {file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"}, + {file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"}, + {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"}, + {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"}, + {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"}, + {file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"}, + {file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"}, + {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"}, + {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"}, + {file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"}, + {file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"}, + {file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"}, + {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"}, + {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"}, + {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"}, + {file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"}, + {file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"}, + {file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"}, + {file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7"}, + {file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019"}, + {file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755"}, + {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"}, +] + [[package]] name = "cachetools" version = "5.3.1" @@ -986,6 +1088,21 @@ lint = ["black (>=22.6.0)", "mdformat (>0.7)", "mdformat-gfm (>=0.3.5)", "ruff ( test = ["pytest"] typing = ["mypy (>=0.990)"] +[[package]] +name = "configargparse" +version = "1.7" +description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." +optional = false +python-versions = ">=3.5" +files = [ + {file = "ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b"}, + {file = "ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1"}, +] + +[package.extras] +test = ["PyYAML", "mock", "pytest"] +yaml = ["PyYAML"] + [[package]] name = "coverage" version = "7.3.0" @@ -1590,6 +1707,56 @@ files = [ {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, ] +[[package]] +name = "flask" +version = "2.3.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Flask-2.3.2-py3-none-any.whl", hash = "sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0"}, + {file = "Flask-2.3.2.tar.gz", hash = "sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=2.3.3" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-basicauth" +version = "0.2.0" +description = "HTTP basic access authentication for Flask." +optional = false +python-versions = "*" +files = [ + {file = "Flask-BasicAuth-0.2.0.tar.gz", hash = "sha256:df5ebd489dc0914c224419da059d991eb72988a01cdd4b956d52932ce7d501ff"}, +] + +[package.dependencies] +Flask = "*" + +[[package]] +name = "flask-cors" +version = "4.0.0" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[package.dependencies] +Flask = ">=0.9" + [[package]] name = "flatbuffers" version = "23.5.26" @@ -1724,6 +1891,164 @@ smb = ["smbprotocol"] ssh = ["paramiko"] tqdm = ["tqdm"] +[[package]] +name = "gevent" +version = "23.7.0" +description = "Coroutine-based network library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "gevent-23.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:add904a7ef960cd4e133e61eb7413982c5e4203928160be1c09752ac06a25e71"}, + {file = "gevent-23.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bd9ea1b5fbdc7e5921a9e515f34a450eb3927a902253a33caedcce2d19d7d96"}, + {file = "gevent-23.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c7c349aa23d67cf5cc3b2c87aaedcfead976d0577b1cfcd07ffeba63baba79c"}, + {file = "gevent-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c92b837b60e850c50fc6d723d1e363e786d37fd9d51e564e07df52ad5e8a86d4"}, + {file = "gevent-23.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6a51a8e3cdaa6901e47d56f84cb5f92b1bf3deea920bce69cf7a245df16159ac"}, + {file = "gevent-23.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1dba07207b15b371e50372369edf256a142cb5cdf8599849cbf8660327efa06"}, + {file = "gevent-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:34086bcc1252ae41e1cb81cf13c4a7678031595c12f4e9a1c3d0ab433f20826a"}, + {file = "gevent-23.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5da07d65dfa23fe419c37cea110bf951b42af6bf3a1fff244043a75c9185dbd5"}, + {file = "gevent-23.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4d7be3352126458cc818309ca6a3b678c209b1ae33e56b6975c6a8309f2068"}, + {file = "gevent-23.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76ca6f893953ab898ebbff5d772103318a85044e55d0bad401d6b49d71bb76e7"}, + {file = "gevent-23.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aeb1511cf0786152af741c47ee462dac81b57bbd1fbbe08ab562b6c8c9ad75ed"}, + {file = "gevent-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:919423e803939726c99ab2d29ea46b8676af549cee72d263f2b24758ec607b2c"}, + {file = "gevent-23.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cea93f4f77badbddc711620cca164ad75c74056603908e621a5ba1b97adbc39c"}, + {file = "gevent-23.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dec7b08daf08385fb281b81ec2e7e703243975d867f40ae0a8a3e30b380eb9ea"}, + {file = "gevent-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:f522b6b015f1bfa9d8d3716ddffb23e3d4a8933df3e4ebf0a29a65a9fa74382b"}, + {file = "gevent-23.7.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:746a1e12f280dab07389e6709164b1e1a6caaf50493ea5b1dcaa73cff005174c"}, + {file = "gevent-23.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b230007a665d2cf5cf8878c9f56a2b8bacbdc4fe0235afc5269b71cd00528e5"}, + {file = "gevent-23.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1d2f1e67d04fde47ca2deac89733df28ef3a7ec1d7359a79f57d4778cced16d"}, + {file = "gevent-23.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:debc177e88a8c876cb1a4d974f985d03670177bdc61e1c084a8d525f1a50b12d"}, + {file = "gevent-23.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b3dd449c80814357f6568eb095a2be2421b805d59fa97c65094707e04a181f9"}, + {file = "gevent-23.7.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:769e8811ded08fe7d8b09ad8ebb72d47aecc112411e0726e7296b7ed187ed629"}, + {file = "gevent-23.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11b9bb0bce45170ff992760385a86e6955ccb88dba4a82a64d5ce9459290d8d6"}, + {file = "gevent-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e0d76a7848726e0646324a1adc011355dcd91875e7913badd1ada2e5eeb8a6e"}, + {file = "gevent-23.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a226b42cb9a49580ca7729572a4f8289d1fa28cd2529c9f4eed3e14b995d1c9c"}, + {file = "gevent-23.7.0-cp38-cp38-win32.whl", hash = "sha256:1234849b0bc4df560924aa92f7c01ca3f310677735fb508a2b0d7a61bb946916"}, + {file = "gevent-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:a8f62e8d37913512823923e05607a296389aeb50ccca8a271ae7cedb5b17faeb"}, + {file = "gevent-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369241d1a6a3fe3ef4eba454b71e0168026560c5344fc4bc37196867041982ac"}, + {file = "gevent-23.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:94b013587f7c4697d620c129627f7b12d7d9f6e40ab198635891ca2098cd8556"}, + {file = "gevent-23.7.0-cp39-cp39-win32.whl", hash = "sha256:83b6d61a8e9da25edb304ca7fba19ee57bb1ffa801f9df3e668bfed7bb8386cb"}, + {file = "gevent-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c284390f0f6d0b5be3bf805fa8e0ae1329065f2b0ac5af5423c67183197deb8"}, + {file = "gevent-23.7.0.tar.gz", hash = "sha256:d0d3630674c1b344b256a298ab1ff43220f840b12af768131b5d74e485924237"}, +] + +[package.dependencies] +cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} +greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""} +"zope.event" = "*" +"zope.interface" = "*" + +[package.extras] +dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] +docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] +monitor = ["psutil (>=5.7.0)"] +recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests", "setuptools"] + +[[package]] +name = "geventhttpclient" +version = "2.0.9" +description = "http client library for gevent" +optional = false +python-versions = "*" +files = [ + {file = "geventhttpclient-2.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f4dcd420c74f68ffc493abdc0964583f762e0f03c9ccd62b8dcfa589f8cabe49"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe8766994434ed6dc807d2e5726e9cec536b5593885b50598fe763908242054"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c23aacbe40f767d4bfb4215b51302d68f9611f2e730521e65c7239854177c30f"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f9ead0e6eb8302b394744c8ad7df71b1482e40402ecc208b446dba870bbafa1"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15393cf588d24bf90b430299733ed161829065d14df99db250b40a83ec7978aa"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41b35daa9fe8217f295bc162492260daf4d4e1d9e3e8ed1fa2085a003cb5bb86"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0373b1d920cb2c1d65913258e2abe1534f5c6d1e687ed9322ad47858d75fd7d0"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:344f22ce6cc959e67c32c9992422ee623b2e92da80b7774f939a03a8f66a823b"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2f467fc073631dd7c13f289ab59e82ae9b181a1faabcc8b23be48a48e8e45ba9"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:9f1395906e79b7d3db97199625ae9050c1249e2cb8073bb32495c375af351849"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3c8f035c55f8db9b3a976d813dd20965ecf957e268e420e66b88cba81a66f3ac"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-win32.whl", hash = "sha256:372776cdcad9a34a8635da4002f1614a36269ee02e968750c4fbde6ea83360d7"}, + {file = "geventhttpclient-2.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:9a11b88ce228f91bd47acacb573acbcdf8276b933e2fd7e4f7aa6259c279271e"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273cde2893c13cbdfcad46e6a0fb906aa3f6b050e712cba0e46cfa59aca4c330"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba0f8863526a2cf44567fda5981da90278583d3006aef02482a92b7d7e38e610"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8da5696d055883cef55fe52246ea2a686bae865d4d38900f67c7c3df7d01eaa"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e25e14c91898217f3a69346c2704e4629634b3429cdff226e716e6d830c402e1"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46b08a7de94a3db62ebeb41e68097db1e3836eef2e2febccb2d710add8943838"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e33f23168824338a84e05e47808a48895e78e77c5d2ec353a6e77a0b727057"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f505a7060aeafd7ed94acc8c234690717ed03aa6421580847ade8d4a6d8741c1"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bc0cd15d51b5817efeef98d6ffe51bebd371c0c81731b239d0c6e81923c3b986"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9a7e72d38e3a4d4f84c11dbfd74b70e70c3c9099ea7336a6c6227e0628c83a67"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:62edef5141ea260caa91618b133d8888d9af6767e11a2e972dbb5be0e297ff8e"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:40cc9acfb7096b8fa3a4c4634dcc0a1cfc6a0a5501c597d1c4a5574fde1bb1be"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-win32.whl", hash = "sha256:44e7de9925356486988f72b239d1608597b6d1462664a7fa7ad5b8704a3f6f4e"}, + {file = "geventhttpclient-2.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:03b3c64fcd16795a5acbdf8588e05a58fde498cd550285cf3ea6e1c6cc28378c"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:be198e668c8573e4cc7fa8e6b71e32a94765c424bc6a8cdd5fc23ab23769864c"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f550e6ceb67a6cac9e42915612a10c0f799666fa309be796db65585690d8262"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5210e91b1a1d9f2bcd414238cae2593b66172098db7bde396ae2fdb8e658eb85"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97f4c7f57bf24b669bc2e71de725067a4b73f280fc20875fd8c7cee712b754f3"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01869edfc3f9b892dfdd0b3ef102c97dd250725e8364de2e450d9ee2e3bf74db"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a85f9d272af4bc599a54496cb420ee0654c35023d6cd7f2a49c36d5a5d30e375"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:9f78d0bda2583a7f9d87cc987ab9976bf4e85d467af7d8fa39f51bed1918e812"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:2e8013742b22586c25c797d7d7bfb8882b2eed43051f28f7617ea302cf4e5098"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:1eb410b5522ef617923470f036774388c191eda3ae47abf5a175f628cff75928"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-win32.whl", hash = "sha256:12de78429c420ac3dbfd4e89fbd1d9d2cf9a60c06a82fc39a398f92d5f717c95"}, + {file = "geventhttpclient-2.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:5eb747774ed72467b3b77913bfe041f0be4a22958dd7538a654bf24107d1b917"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:36e9c7e4f86db9335123a12b343c2b91d8d7fe4724428ac418bcfcd471248bc9"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a7d8a7d7ee6b08e44e58e92e47ca4b50edf8ed64b8906bd0a6cbb481aeae69b"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:186d3e4f549a004f0ae8857db81f05798d48dd9e6cbfa97dfa46f6177883458d"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c0ed350c0f4598f6d07d6fdc2d3546a1611ebf15fcdc83bc77dea6d22f2ef38"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c557aeb45a005c72200842fddfb507bbd7f2ec9296059c7ea3977ea226d4e7"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6c9ea74c83e94ea7c9869e383f93fa20a5fbde9cfb020400cdb75c39e029507a"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9b072c041993b3242aeccce0d436bcb951f83e83517d5f5c0b9b97efe6ee273e"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d17ea1282fef41aa1b43e193869f0b9119fab919fe90092dfa5754fd6a012982"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4941aa5e45479a6595469754d6395e5d1a76cf555d0d111aa4c128d8f4ebd41b"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-win32.whl", hash = "sha256:2d36b3a241a4c64a094622c4da67b98141295ab9ba24a79671235bb8735047f5"}, + {file = "geventhttpclient-2.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:f7a99e0976ac64d21f54be87083b6ec440753db50db36d83b2b2b0b6edaacad7"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f46848b5c6610c671a7258757bf4d34032c56a80ead880e69890d9ef5c1e63a1"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b671b736468e35e588f24d2219d9f84f3c69c466fdc76ce47774106eb3af2c10"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b290897e50e0f378d4cabdafffe2f51950d1c820722ee39b37657eda7339a3a8"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41cad9ddc365edf77cb62a4e3a8168dee9ee37109b410a42c5ec0691403dbe37"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0b69bc9632827b108aba11d85e7c604290e413486e7dc9ddf471303ceab4381"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:988121fdd5b8939c4071ce45b26f9e3aa1cb050e3087ee02b28e7c21bcada35c"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db4db1ceaa88d648c43a318af16690fb59f13ad1fc48603e054c563490950034"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:192be8b440e6512c384cc5be98b3b3e8b39dcae0f7c10aee3c3491f30a20e676"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ffed7591e612cb80935ed11c1fb0a752aed3f014a48b9404cd0c77182e82335c"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:c2b06ccac902fb243384da12692be240e7a9ddcd2efedcde2444872546ada83c"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8257d9538a1d342a2052c0df099a318d7611562a4725985ac652a356c5cd45c9"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-win32.whl", hash = "sha256:ce61c0be828e10dbfac91ed7160f651ecc2fbad44ed1815eaac6e9d41e09a87a"}, + {file = "geventhttpclient-2.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:aa2b47489bd229fce3ab626c370eb8d18fd6116611552de9d8979c37238fc62b"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a16bde611a76ada61c9af9acc20b88d5ee6c025deede2680fde084838a3bfbae"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:780d04ea934e254af9de90402f29804e6a3455c3bfa44acc1e8ccb65ce5115a9"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3033399fcbe1fbafbe6ff56afb587725aa993ab549845ebbd8a2206be6082cb"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d32d3f6f413a6a16ac3bfe4cb53f0935c52f33820d2da21d9929ec6a910519b"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6684b82fd3719d4823ef2eda1edd5bfbf3154e5c2241db0ed858ffb99f8a4ad9"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38f5c6cfa9387ad741f0b5033b6de12eeb670870149a50b7a64444c84f8e7167"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d654ae061852c842a75b1a38f42b5523389475fe9015de732a55242f6d221e"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:491ee724ba4057b076610be20da6345a66c630d82ed52f12014ce8bad5a699ab"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eab149b7119ac7a6243cad85173f9f9e9ae004e299358181641dddc39233a2d1"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f6ad2c5590a68b7e959ab5f4adab78008ba3a661482c1fb56a38eba09744b899"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47bb3025950cc9bf0fd627b101380cf5b0efbbaa591198b851352b24966931cb"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-win32.whl", hash = "sha256:549756ac0e3cc916d6830f0d6147803a5c93beae96f8504f685e4e841d6ace29"}, + {file = "geventhttpclient-2.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:e3133d3e492c5dfb78f9c0f87e1ede78545e7f4bb4851a063df2321669540004"}, + {file = "geventhttpclient-2.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3fca4415c9d9282a8b5f4de03661be3b1083b97b3e43a1f4b56cec4b933b9705"}, + {file = "geventhttpclient-2.0.9-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b85ca16b34170787a48ac2c69622bba2f2a3541ee55b442576a12f63181f13e"}, + {file = "geventhttpclient-2.0.9-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:711f3522ce20d2de5abe22cafdeee0896f9f682de7690622510c5f8a859c50d2"}, + {file = "geventhttpclient-2.0.9-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96479cafe8cca4e24a1ba53efe3c3c6cf9f10e8cb0398f4cbcf23b4233e103f2"}, + {file = "geventhttpclient-2.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d742f4288844ec5dbed2af4396fca8d37b5f4d2730ca31fe3575d67a2910e28f"}, + {file = "geventhttpclient-2.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:53e929c7920fb7841d4b2cde50ed3fdde803948977a333de33a924a9ccfccd38"}, + {file = "geventhttpclient-2.0.9-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4693365990aa6d5cb104cea80dd831b8032f6a4f77790fa8b837fc25e86f8e5"}, + {file = "geventhttpclient-2.0.9-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99bb292a4f4fa4a23d22e6bf1799530d0cb675dae87d81d77079abe98f71150d"}, + {file = "geventhttpclient-2.0.9-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4533b7023a90cb9766213f70d15bc76bb66b7b441c3976f27e254403a6c738fa"}, + {file = "geventhttpclient-2.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a9b6341c937d01ba48028a3d605fded6fd2eed692bb1ddc941d2aa3dd317eed9"}, + {file = "geventhttpclient-2.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a05a3345796accd0a9acb3fc133a0e4cd949d3e63becec34fcb2e8d001dd92fe"}, + {file = "geventhttpclient-2.0.9-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32cc9bd3518aa0772065175aa6eddadff8b05688683940bfd583cb468b518987"}, + {file = "geventhttpclient-2.0.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e7185e91b52f6d2cdab849ba24c8ac24b3bd1ad8eb9917e4ed86f4e93f718b1"}, + {file = "geventhttpclient-2.0.9-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62a47451abf94422b3f0efda4f147111f2f4a75f7e55db974cf4b35acaee0131"}, + {file = "geventhttpclient-2.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dc75486b0847c185856f981b728a2bbfe09e8a754676e57a8549660157918d2f"}, + {file = "geventhttpclient-2.0.9.tar.gz", hash = "sha256:8abc39d346e923bd6a7b405d38dd01e19146594b6304032f382eda8b0f631513"}, +] + +[package.dependencies] +brotli = "*" +certifi = "*" +gevent = ">=0.13" +six = "*" + [[package]] name = "gitdb" version = "4.0.10" @@ -2751,6 +3076,17 @@ qtconsole = ["qtconsole"] test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + [[package]] name = "jaraco-classes" version = "3.3.0" @@ -2971,7 +3307,7 @@ full = ["aiohttp", "black (==22.3.0)", "docker", "filelock", "flake8 (==4.0.1)", name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, @@ -3225,6 +3561,33 @@ typing-extensions = ">=4.5.0" [package.extras] server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (>=1.6.1)", "uvicorn (>=0.22.0)"] +[[package]] +name = "locust" +version = "2.16.1" +description = "Developer friendly load testing framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "locust-2.16.1-py3-none-any.whl", hash = "sha256:d0f01f9fca6a7d9be987b32185799d9e219fce3b9a3b8250ea03e88003335804"}, + {file = "locust-2.16.1.tar.gz", hash = "sha256:cd54f179b679ae927e9b3ffd2b6a7c89c1078103cfbe96b4dd53c7872774b619"}, +] + +[package.dependencies] +ConfigArgParse = ">=1.0" +flask = ">=2.0.0" +Flask-BasicAuth = ">=0.2.0" +Flask-Cors = ">=3.0.10" +gevent = ">=20.12.1" +geventhttpclient = ">=2.0.2" +msgpack = ">=0.6.2" +psutil = ">=5.6.7" +pywin32 = {version = "*", markers = "platform_system == \"Windows\""} +pyzmq = ">=22.2.1,<23.0.0 || >23.0.0" +requests = ">=2.23.0" +roundrobin = ">=0.0.2" +typing-extensions = ">=3.7.4.3" +Werkzeug = ">=2.0.0" + [[package]] name = "loguru" version = "0.7.0" @@ -3644,6 +4007,78 @@ olefile = ">=0.46" [package.extras] rtf = ["compressed-rtf (>=1.0.5)"] +[[package]] +name = "msgpack" +version = "1.0.5" +description = "MessagePack serializer" +optional = false +python-versions = "*" +files = [ + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"}, + {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"}, + {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"}, + {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"}, + {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"}, + {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"}, + {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"}, + {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"}, + {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"}, + {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"}, + {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"}, + {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"}, + {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"}, + {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"}, + {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"}, + {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"}, +] + [[package]] name = "multidict" version = "6.0.4" @@ -6046,6 +6481,16 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "roundrobin" +version = "0.0.4" +description = "Collection of roundrobin utilities" +optional = false +python-versions = "*" +files = [ + {file = "roundrobin-0.0.4.tar.gz", hash = "sha256:7e9d19a5bd6123d99993fb935fa86d25c88bb2096e493885f61737ed0f5e9abd"}, +] + [[package]] name = "rsa" version = "4.9" @@ -7624,6 +8069,23 @@ files = [ {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, ] +[[package]] +name = "werkzeug" +version = "2.3.7" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-2.3.7-py3-none-any.whl", hash = "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528"}, + {file = "werkzeug-2.3.7.tar.gz", hash = "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + [[package]] name = "wheel" version = "0.41.1" @@ -7879,6 +8341,71 @@ files = [ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +[[package]] +name = "zope-event" +version = "5.0" +description = "Very basic event publishing system" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, + {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, +] + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["Sphinx"] +test = ["zope.testrunner"] + +[[package]] +name = "zope-interface" +version = "6.0" +description = "Interfaces for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zope.interface-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f299c020c6679cb389814a3b81200fe55d428012c5e76da7e722491f5d205990"}, + {file = "zope.interface-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee4b43f35f5dc15e1fec55ccb53c130adb1d11e8ad8263d68b1284b66a04190d"}, + {file = "zope.interface-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a158846d0fca0a908c1afb281ddba88744d403f2550dc34405c3691769cdd85"}, + {file = "zope.interface-6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72f23bab1848edb7472309e9898603141644faec9fd57a823ea6b4d1c4c8995"}, + {file = "zope.interface-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48f4d38cf4b462e75fac78b6f11ad47b06b1c568eb59896db5b6ec1094eb467f"}, + {file = "zope.interface-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:87b690bbee9876163210fd3f500ee59f5803e4a6607d1b1238833b8885ebd410"}, + {file = "zope.interface-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2363e5fd81afb650085c6686f2ee3706975c54f331b426800b53531191fdf28"}, + {file = "zope.interface-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af169ba897692e9cd984a81cb0f02e46dacdc07d6cf9fd5c91e81f8efaf93d52"}, + {file = "zope.interface-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa90bac61c9dc3e1a563e5babb3fd2c0c1c80567e815442ddbe561eadc803b30"}, + {file = "zope.interface-6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89086c9d3490a0f265a3c4b794037a84541ff5ffa28bb9c24cc9f66566968464"}, + {file = "zope.interface-6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:809fe3bf1a91393abc7e92d607976bbb8586512913a79f2bf7d7ec15bd8ea518"}, + {file = "zope.interface-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:0ec9653825f837fbddc4e4b603d90269b501486c11800d7c761eee7ce46d1bbb"}, + {file = "zope.interface-6.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:790c1d9d8f9c92819c31ea660cd43c3d5451df1df61e2e814a6f99cebb292788"}, + {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b39b8711578dcfd45fc0140993403b8a81e879ec25d53189f3faa1f006087dca"}, + {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eba51599370c87088d8882ab74f637de0c4f04a6d08a312dce49368ba9ed5c2a"}, + {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee934f023f875ec2cfd2b05a937bd817efcc6c4c3f55c5778cbf78e58362ddc"}, + {file = "zope.interface-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:042f2381118b093714081fd82c98e3b189b68db38ee7d35b63c327c470ef8373"}, + {file = "zope.interface-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dfbbbf0809a3606046a41f8561c3eada9db811be94138f42d9135a5c47e75f6f"}, + {file = "zope.interface-6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:424d23b97fa1542d7be882eae0c0fc3d6827784105264a8169a26ce16db260d8"}, + {file = "zope.interface-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e538f2d4a6ffb6edfb303ce70ae7e88629ac6e5581870e66c306d9ad7b564a58"}, + {file = "zope.interface-6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12175ca6b4db7621aedd7c30aa7cfa0a2d65ea3a0105393e05482d7a2d367446"}, + {file = "zope.interface-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3d7dfd897a588ec27e391edbe3dd320a03684457470415870254e714126b1f"}, + {file = "zope.interface-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b3f543ae9d3408549a9900720f18c0194ac0fe810cecda2a584fd4dca2eb3bb8"}, + {file = "zope.interface-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0583b75f2e70ec93f100931660328965bb9ff65ae54695fb3fa0a1255daa6f2"}, + {file = "zope.interface-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:23ac41d52fd15dd8be77e3257bc51bbb82469cf7f5e9a30b75e903e21439d16c"}, + {file = "zope.interface-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99856d6c98a326abbcc2363827e16bd6044f70f2ef42f453c0bd5440c4ce24e5"}, + {file = "zope.interface-6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1592f68ae11e557b9ff2bc96ac8fc30b187e77c45a3c9cd876e3368c53dc5ba8"}, + {file = "zope.interface-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4407b1435572e3e1610797c9203ad2753666c62883b921318c5403fb7139dec2"}, + {file = "zope.interface-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:5171eb073474a5038321409a630904fd61f12dd1856dd7e9d19cd6fe092cbbc5"}, + {file = "zope.interface-6.0.tar.gz", hash = "sha256:aab584725afd10c710b8f1e6e208dbee2d0ad009f57d674cb9d1b3964037275d"}, +] + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["Sphinx", "repoze.sphinx.autointerface"] +test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] + [[package]] name = "zstandard" version = "0.21.0" @@ -7945,4 +8472,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "8ad605e7ea30f2819dbc03eac6c2e67576a98d1efa4890912414a7568fc27441" +content-hash = "5c89b26e701f4243740908a2e5690a17a7203a3313c977093b418a53510ec110" diff --git a/pyproject.toml b/pyproject.toml index 4e7a34ac0..3e6c6ae6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,6 +103,7 @@ types-appdirs = "^1.4.3.5" types-pyyaml = "^6.0.12.8" types-python-jose = "^3.3.4.8" types-passlib = "^1.7.7.13" +locust = "^2.16.1" [tool.poetry.extras] From d393a66b2f2592d7efcb989bdf9136a501dd80a0 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 09:11:52 -0300 Subject: [PATCH 038/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20handle?= =?UTF-8?q?=20ImportError=20when=20importing=20celery.result.AsyncResult?= =?UTF-8?q?=20to=20allow=20building=20the=20vertex=20locally=20when=20Cele?= =?UTF-8?q?ry=20is=20not=20installed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index 87c8fa851..bbd2bbb65 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -11,7 +11,7 @@ import inspect import types from typing import Any, Dict, List, Optional from typing import TYPE_CHECKING -from celery.result import AsyncResult + if TYPE_CHECKING: from langflow.graph.edge.base import Edge @@ -180,6 +180,11 @@ class Vertex: return self._built_object # Check if there's a task_id, which means it was sent to a Celery worker + try: + from celery.result import AsyncResult + except ImportError: + # If Celery is not installed, just build the vertex locally + return self.build() if self.is_task and self.task_id is not None: result = AsyncResult(self.task_id).get( timeout=timeout From ab6515515d993ae335a960289d6af6ea518dd532 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 10:15:21 -0300 Subject: [PATCH 039/213] =?UTF-8?q?=F0=9F=90=9B=20fix(types.py):=20add=20c?= =?UTF-8?q?onditional=20check=20for=20task=5Fid=20and=20is=5Ftask=20to=20i?= =?UTF-8?q?mprove=20object=20representation=20in=20CustomComponentVertex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index 597998515..672bd8ff9 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -255,5 +255,10 @@ class CustomComponentVertex(Vertex): super().__init__(data, base_type="custom_components", is_task=True) def _built_object_repr(self): + if self.task_id and self.is_task: + if task := self.get_task(): + return str(task.info) + else: + return f"Task {self.task_id} is not running" if self.artifacts and "repr" in self.artifacts: return self.artifacts["repr"] or super()._built_object_repr() From 24344ee0fa379b761fda4e114e38a28933257902 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 10:15:38 -0300 Subject: [PATCH 040/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20remove?= =?UTF-8?q?=20unnecessary=20import=20statement=20for=20celery.result.Async?= =?UTF-8?q?Result=20=F0=9F=94=80=20refactor(base.py):=20refactor=20the=20b?= =?UTF-8?q?uild=20method=20in=20Vertex=20class=20to=20improve=20readabilit?= =?UTF-8?q?y=20and=20remove=20redundant=20code=20=E2=9C=A8=20feat(base.py)?= =?UTF-8?q?:=20add=20get=5Ftask=20method=20to=20Vertex=20class=20to=20retr?= =?UTF-8?q?ieve=20the=20task=20from=20celery=20using=20task=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/base.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index bbd2bbb65..50adc7764 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -73,6 +73,13 @@ class Vertex: self.base_type = base_type break + def get_task(self): + # using the task_id, get the task from celery + # and return it + from celery.result import AsyncResult + + return AsyncResult(self.task_id) + def _build_params(self): # sourcery skip: merge-list-append, remove-redundant-if # Some params are required, some are optional @@ -179,16 +186,9 @@ class Vertex: if self._built: return self._built_object - # Check if there's a task_id, which means it was sent to a Celery worker - try: - from celery.result import AsyncResult - except ImportError: - # If Celery is not installed, just build the vertex locally - return self.build() if self.is_task and self.task_id is not None: - result = AsyncResult(self.task_id).get( - timeout=timeout - ) # Blocking until result is ready or timeout + task = self.get_task() + result = task.get(timeout=timeout) if result is not None: # If result is ready self._update_built_object_and_artifacts(result) return self._built_object @@ -197,7 +197,8 @@ class Vertex: pass # If there's no task_id, build the vertex locally - return self.build() + self.build() + return self._built_object def _build_node_and_update_params(self, key, node): """ From 2dd43d3f994423120c5990e7d54f81066b928bcf Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 10:15:49 -0300 Subject: [PATCH 041/213] =?UTF-8?q?=F0=9F=90=9B=20fix(chat.py):=20add=20su?= =?UTF-8?q?pport=20for=20running=20celery=20tasks=20for=20building=20verti?= =?UTF-8?q?ces=20and=20fallback=20to=20local=20build=20if=20celery=20task?= =?UTF-8?q?=20fails?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/chat.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index 2cec3b898..d28dfc79c 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -123,7 +123,10 @@ async def stream_build(flow_id: str): "log": f"Building node {vertex.vertex_type}", } yield str(StreamData(event="log", data=log_dict)) - vertex.build() + if vertex.is_task: + vertex = try_running_celery_task(vertex) + else: + vertex.build() params = vertex._built_object_repr() valid = True logger.debug(f"Building node {str(vertex.vertex_type)}") @@ -179,3 +182,20 @@ async def stream_build(flow_id: str): except Exception as exc: logger.error(f"Error streaming build: {exc}") raise HTTPException(status_code=500, detail=str(exc)) + + +def try_running_celery_task(vertex): + # Try running the task in celery + # and set the task_id to the local vertex + # if it fails, run the task locally + try: + from langflow.worker import build_vertex + + task = build_vertex.delay(vertex) + vertex.task_id = task.id + except Exception as exc: + logger.exception(exc) + logger.error("Error running task in celery, running locally") + vertex.task_id = None + vertex.build() + return vertex From 82e6b38ca4941eafb6430e3e03d08f882d183465 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 10:16:00 -0300 Subject: [PATCH 042/213] =?UTF-8?q?=F0=9F=90=9B=20fix(worker.py):=20add=20?= =?UTF-8?q?exception=20handling=20and=20retry=20mechanism=20to=20build=5Fv?= =?UTF-8?q?ertex=20task=20to=20handle=20SoftTimeLimitExceeded=20error=20an?= =?UTF-8?q?d=20retry=20the=20task=20up=20to=203=20times=20with=20a=20count?= =?UTF-8?q?down=20of=202=20seconds=20=E2=9C=A8=20feat(worker.py):=20add=20?= =?UTF-8?q?import=20statement=20for=20SoftTimeLimitExceeded=20exception=20?= =?UTF-8?q?from=20celery.exceptions=20module=20to=20handle=20SoftTimeLimit?= =?UTF-8?q?Exceeded=20error=20in=20build=5Fvertex=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/worker.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 67eaf6b3b..f8831df76 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -1,6 +1,7 @@ from langflow.core.celery_app import celery_app from typing import Any, Dict, Optional from typing import TYPE_CHECKING +from celery.exceptions import SoftTimeLimitExceeded if TYPE_CHECKING: from langflow.graph.vertex.base import Vertex @@ -11,13 +12,19 @@ def test_celery(word: str) -> str: return f"test task return {word}" -@celery_app.task -def build_vertex(vertex: "Vertex") -> "Vertex": +@celery_app.task(bind=True, soft_time_limit=30, max_retries=3) +def build_vertex(self, vertex: "Vertex") -> "Vertex": """ Build a vertex """ - vertex.build() - return vertex + try: + vertex.task_id = self.request.id + vertex.build() + return vertex + except SoftTimeLimitExceeded as e: + raise self.retry( + exc=SoftTimeLimitExceeded("Task took too long"), countdown=2 + ) from e @celery_app.task(acks_late=True) From f3343b81c567b87baadad4bdc28d2e7be3bd4c97 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 10:22:59 -0300 Subject: [PATCH 043/213] Poetry lock --- poetry.lock | 391 ++++------------------------------------------------ 1 file changed, 30 insertions(+), 361 deletions(-) diff --git a/poetry.lock b/poetry.lock index e7cfa2bc8..d8b5692c1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiofiles" version = "23.2.1" description = "File support for asyncio." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -16,7 +15,6 @@ files = [ name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -125,7 +123,6 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -140,7 +137,6 @@ frozenlist = ">=1.1.0" name = "aiostream" version = "0.4.5" description = "Generator-based operators for asynchronous iteration" -category = "main" optional = false python-versions = "*" files = [ @@ -185,7 +181,6 @@ vine = ">=5.0.0" name = "anthropic" version = "0.3.10" description = "Client library for the anthropic API" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -205,7 +200,6 @@ typing-extensions = ">=4.5,<5" name = "anyio" version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -227,7 +221,6 @@ trio = ["trio (<0.22)"] name = "appdirs" version = "1.4.4" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" optional = false python-versions = "*" files = [ @@ -239,7 +232,6 @@ files = [ name = "appnope" version = "0.1.3" description = "Disable App Nap on macOS >= 10.9" -category = "dev" optional = false python-versions = "*" files = [ @@ -251,7 +243,6 @@ files = [ name = "argilla" version = "0.0.1" description = "" -category = "main" optional = false python-versions = "*" files = [ @@ -263,7 +254,6 @@ files = [ name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -281,7 +271,6 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "asttokens" version = "2.2.1" description = "Annotate AST trees with source code positions" -category = "dev" optional = false python-versions = "*" files = [ @@ -299,7 +288,6 @@ test = ["astroid", "pytest"] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -311,7 +299,6 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -330,7 +317,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "authlib" version = "1.2.1" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." -category = "main" optional = false python-versions = "*" files = [ @@ -345,7 +331,6 @@ cryptography = ">=3.2" name = "backcall" version = "0.2.0" description = "Specifications for callback functions passed in to an API" -category = "dev" optional = false python-versions = "*" files = [ @@ -357,7 +342,6 @@ files = [ name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -403,7 +387,6 @@ typecheck = ["mypy"] name = "beautifulsoup4" version = "4.12.2" description = "Screen-scraping library" -category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -433,7 +416,6 @@ files = [ name = "black" version = "23.7.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -480,7 +462,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "bleach" version = "6.0.0" description = "An easy safelist-based HTML-sanitizing tool." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -601,7 +582,6 @@ files = [ name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -669,7 +649,6 @@ zstd = ["zstandard (==0.21.0)"] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -681,7 +660,6 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" files = [ @@ -758,7 +736,6 @@ pycparser = "*" name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -770,7 +747,6 @@ files = [ name = "charset-normalizer" version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -855,7 +831,6 @@ files = [ name = "chromadb" version = "0.3.26" description = "Chroma." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -885,7 +860,6 @@ uvicorn = {version = ">=0.18.3", extras = ["standard"]} name = "click" version = "8.1.6" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -914,7 +888,6 @@ click = ">=7" name = "click-log" version = "0.4.0" description = "Logging integration for Click" -category = "main" optional = false python-versions = "*" files = [ @@ -964,7 +937,6 @@ testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] name = "clickhouse-connect" version = "0.6.8" description = "ClickHouse Database Core Driver for Python, Pandas, and Superset" -category = "main" optional = false python-versions = "~=3.7" files = [ @@ -1054,7 +1026,6 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"] name = "cohere" version = "4.20.1" description = "" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -1074,7 +1045,6 @@ urllib3 = ">=1.26,<3" name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -1086,7 +1056,6 @@ files = [ name = "coloredlogs" version = "15.0.1" description = "Colored terminal output for Python's logging module" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1104,7 +1073,6 @@ cron = ["capturer (>=2.4)"] name = "comm" version = "0.1.4" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1139,7 +1107,6 @@ yaml = ["PyYAML"] name = "coverage" version = "7.3.0" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1207,7 +1174,6 @@ toml = ["tomli"] name = "cryptography" version = "41.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1253,7 +1219,6 @@ test-randomorder = ["pytest-randomly"] name = "ctransformers" version = "0.2.22" description = "Python bindings for the Transformer models implemented in C/C++ using GGML library." -category = "main" optional = true python-versions = "*" files = [ @@ -1274,7 +1239,6 @@ tests = ["pytest"] name = "dataclasses-json" version = "0.5.14" description = "Easily serialize dataclasses to and from JSON." -category = "main" optional = false python-versions = ">=3.7,<3.13" files = [ @@ -1290,7 +1254,6 @@ typing-inspect = ">=0.4.0,<1" name = "debugpy" version = "1.6.7.post1" description = "An implementation of the Debug Adapter Protocol for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1318,7 +1281,6 @@ files = [ name = "decorator" version = "5.1.1" description = "Decorators for Humans" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1330,7 +1292,6 @@ files = [ name = "deprecated" version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1348,7 +1309,6 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] name = "deprecation" version = "2.1.0" description = "A library to handle automated deprecations" -category = "main" optional = false python-versions = "*" files = [ @@ -1363,7 +1323,6 @@ packaging = "*" name = "dill" version = "0.3.7" description = "serialize all of Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1378,7 +1337,6 @@ graph = ["objgraph (>=1.7.2)"] name = "diskcache" version = "5.6.1" description = "Disk Cache -- Disk and file backed persistent cache." -category = "main" optional = true python-versions = ">=3" files = [ @@ -1390,7 +1348,6 @@ files = [ name = "distro" version = "1.8.0" description = "Distro - an OS platform information API" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1402,7 +1359,6 @@ files = [ name = "dnspython" version = "2.4.2" description = "DNS toolkit" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1422,7 +1378,6 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"] name = "docarray" version = "0.21.1" description = "The data structure for unstructured data" -category = "main" optional = false python-versions = "*" files = [ @@ -1451,7 +1406,6 @@ weaviate = ["weaviate-client (>=3.9.0,<3.10.0)"] name = "docker" version = "6.1.3" description = "A Python library for the Docker Engine API." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1473,7 +1427,6 @@ ssh = ["paramiko (>=2.4.3)"] name = "docstring-parser" version = "0.15" description = "Parse Python docstrings in reST, Google and Numpydoc format" -category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1485,7 +1438,6 @@ files = [ name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1497,7 +1449,6 @@ files = [ name = "dotty-dict" version = "1.3.1" description = "Dictionary wrapper for quick access to deeply nested keys." -category = "main" optional = false python-versions = ">=3.5,<4.0" files = [ @@ -1509,7 +1460,6 @@ files = [ name = "duckdb" version = "0.8.1" description = "DuckDB embedded database" -category = "main" optional = false python-versions = "*" files = [ @@ -1571,7 +1521,6 @@ files = [ name = "ecdsa" version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1590,7 +1539,6 @@ gmpy2 = ["gmpy2"] name = "et-xmlfile" version = "1.1.0" description = "An implementation of lxml.xmlfile for the standard library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1602,7 +1550,6 @@ files = [ name = "exceptiongroup" version = "1.1.3" description = "Backport of PEP 654 (exception groups)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1617,7 +1564,6 @@ test = ["pytest (>=6)"] name = "executing" version = "1.2.0" description = "Get the currently executing AST node of a frame, and other information" -category = "dev" optional = false python-versions = "*" files = [ @@ -1632,7 +1578,6 @@ tests = ["asttokens", "littleutils", "pytest", "rich"] name = "faiss-cpu" version = "1.7.4" description = "A library for efficient similarity search and clustering of dense vectors." -category = "main" optional = false python-versions = "*" files = [ @@ -1667,7 +1612,6 @@ files = [ name = "fake-useragent" version = "1.2.1" description = "Up-to-date simple useragent faker with real world database" -category = "main" optional = false python-versions = "*" files = [ @@ -1682,7 +1626,6 @@ importlib-resources = {version = ">=5.0", markers = "python_version < \"3.10\""} name = "fastapi" version = "0.100.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1702,7 +1645,6 @@ all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" name = "fastavro" version = "1.8.2" description = "Fast read/write of AVRO files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1743,7 +1685,6 @@ zstandard = ["zstandard"] name = "filelock" version = "3.12.2" description = "A platform independent file lock." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1759,7 +1700,6 @@ testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "p name = "filetype" version = "1.2.0" description = "Infer file type and MIME type of any file/buffer. No external dependencies." -category = "main" optional = false python-versions = "*" files = [ @@ -1821,7 +1761,6 @@ Flask = ">=0.9" name = "flatbuffers" version = "23.5.26" description = "The FlatBuffers serialization format for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1851,7 +1790,6 @@ tornado = ">=5.0.0,<7.0.0" name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1922,7 +1860,6 @@ files = [ name = "fsspec" version = "2023.6.0" description = "File-system specification" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2116,7 +2053,6 @@ six = "*" name = "gitdb" version = "4.0.10" description = "Git Object Database" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2131,7 +2067,6 @@ smmap = ">=3.0.1,<6" name = "gitpython" version = "3.1.32" description = "GitPython is a Python library used to interact with Git repositories" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2146,7 +2081,6 @@ gitdb = ">=4.0.1,<5" name = "google-api-core" version = "2.11.1" description = "Google API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2171,7 +2105,6 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] name = "google-api-python-client" version = "2.97.0" description = "Google API Client Library for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2180,7 +2113,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0.dev0" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0.dev0" google-auth = ">=1.19.0,<3.0.0.dev0" google-auth-httplib2 = ">=0.1.0" httplib2 = ">=0.15.0,<1.dev0" @@ -2190,7 +2123,6 @@ uritemplate = ">=3.0.1,<5" name = "google-auth" version = "2.22.0" description = "Google Authentication Library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2216,7 +2148,6 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] name = "google-auth-httplib2" version = "0.1.0" description = "Google Authentication Library: httplib2 transport" -category = "main" optional = false python-versions = "*" files = [ @@ -2233,7 +2164,6 @@ six = "*" name = "google-cloud-aiplatform" version = "1.30.1" description = "Vertex AI API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2242,7 +2172,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.32.0,<2.0.dev0 || >=2.8.dev0,<3.0.0dev", extras = ["grpc"]} google-cloud-bigquery = ">=1.15.0,<4.0.0dev" google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" google-cloud-storage = ">=1.32.0,<3.0.0dev" @@ -2271,7 +2201,6 @@ xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] name = "google-cloud-bigquery" version = "3.11.4" description = "Google BigQuery API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2280,7 +2209,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev", extras = ["grpc"]} google-cloud-core = ">=1.6.0,<3.0.0dev" google-resumable-media = ">=0.6.0,<3.0dev" grpcio = ">=1.47.0,<2.0dev" @@ -2304,7 +2233,6 @@ tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] name = "google-cloud-core" version = "2.3.3" description = "Google Cloud API client core library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2313,7 +2241,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" [package.extras] @@ -2323,7 +2251,6 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)"] name = "google-cloud-resource-manager" version = "1.10.3" description = "Google Cloud Resource Manager API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2332,7 +2259,7 @@ files = [ ] [package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" proto-plus = ">=1.22.0,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" @@ -2341,7 +2268,6 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4 name = "google-cloud-storage" version = "2.10.0" description = "Google Cloud Storage API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2350,7 +2276,7 @@ files = [ ] [package.dependencies] -google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" google-auth = ">=1.25.0,<3.0dev" google-cloud-core = ">=2.3.0,<3.0dev" google-resumable-media = ">=2.3.2" @@ -2363,7 +2289,6 @@ protobuf = ["protobuf (<5.0.0dev)"] name = "google-crc32c" version = "1.5.0" description = "A python wrapper of the C library 'Google CRC32C'" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2444,7 +2369,6 @@ testing = ["pytest"] name = "google-resumable-media" version = "2.5.0" description = "Utilities for Google Media Downloads and Resumable Uploads" -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -2463,7 +2387,6 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] name = "google-search-results" version = "2.4.2" description = "Scrape and search localized results from Google, Bing, Baidu, Yahoo, Yandex, Ebay, Homedepot, youtube at scale using SerpApi.com" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2477,7 +2400,6 @@ requests = "*" name = "googleapis-common-protos" version = "1.60.0" description = "Common protobufs used in Google APIs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2496,7 +2418,6 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] name = "gotrue" version = "1.0.2" description = "Python Client Library for GoTrue" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -2512,7 +2433,6 @@ pydantic = ">=1.10.0,<2.0.0" name = "greenlet" version = "2.0.2" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -2586,7 +2506,6 @@ test = ["objgraph", "psutil"] name = "grpc-google-iam-v1" version = "0.12.6" description = "IAM API client library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2603,7 +2522,6 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 name = "grpcio" version = "1.47.5" description = "HTTP/2-based RPC framework" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2665,7 +2583,6 @@ protobuf = ["grpcio-tools (>=1.47.5)"] name = "grpcio-health-checking" version = "1.47.5" description = "Standard Health Checking Service for gRPC" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2681,7 +2598,6 @@ protobuf = ">=3.12.0" name = "grpcio-reflection" version = "1.47.5" description = "Standard Protobuf Reflection Service for gRPC" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2697,7 +2613,6 @@ protobuf = ">=3.12.0" name = "grpcio-status" version = "1.47.5" description = "Status proto mapping for gRPC" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2714,7 +2629,6 @@ protobuf = ">=3.12.0" name = "grpcio-tools" version = "1.47.5" description = "Protobuf code generator for gRPC" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2775,7 +2689,6 @@ setuptools = "*" name = "gunicorn" version = "21.2.0" description = "WSGI HTTP Server for UNIX" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2796,7 +2709,6 @@ tornado = ["tornado (>=0.2)"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2808,7 +2720,6 @@ files = [ name = "h2" version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" -category = "main" optional = false python-versions = ">=3.6.1" files = [ @@ -2824,7 +2735,6 @@ hyperframe = ">=6.0,<7" name = "hnswlib" version = "0.7.0" description = "hnswlib" -category = "main" optional = false python-versions = "*" files = [ @@ -2838,7 +2748,6 @@ numpy = "*" name = "hpack" version = "4.0.0" description = "Pure-Python HPACK header compression" -category = "main" optional = false python-versions = ">=3.6.1" files = [ @@ -2850,7 +2759,6 @@ files = [ name = "httpcore" version = "0.16.3" description = "A minimal low-level HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2862,17 +2770,16 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "httplib2" version = "0.22.0" description = "A comprehensive HTTP client library." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2887,7 +2794,6 @@ pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0 name = "httptools" version = "0.6.0" description = "A collection of framework independent HTTP protocol utils." -category = "main" optional = false python-versions = ">=3.5.0" files = [ @@ -2935,7 +2841,6 @@ test = ["Cython (>=0.29.24,<0.30.0)"] name = "httpx" version = "0.23.3" description = "The next generation HTTP client." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2952,15 +2857,14 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "huggingface-hub" version = "0.16.4" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -2995,7 +2899,6 @@ typing = ["pydantic", "types-PyYAML", "types-requests", "types-simplejson", "typ name = "humanfriendly" version = "10.0" description = "Human friendly output for text interfaces using Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3024,7 +2927,6 @@ tests = ["freezegun", "pytest", "pytest-cov"] name = "hyperframe" version = "6.0.1" description = "HTTP/2 framing layer for Python" -category = "main" optional = false python-versions = ">=3.6.1" files = [ @@ -3036,7 +2938,6 @@ files = [ name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -3048,7 +2949,6 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3068,7 +2968,6 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "importlib-resources" version = "6.0.1" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3087,7 +2986,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3099,7 +2997,6 @@ files = [ name = "invoke" version = "1.7.3" description = "Pythonic task execution" -category = "main" optional = false python-versions = "*" files = [ @@ -3111,7 +3008,6 @@ files = [ name = "ipykernel" version = "6.25.1" description = "IPython Kernel for Jupyter" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3125,7 +3021,7 @@ comm = ">=0.1.1" debugpy = ">=1.6.5" ipython = ">=7.23.1" jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" @@ -3145,7 +3041,6 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio" name = "ipython" version = "8.14.0" description = "IPython: Productive Interactive Computing" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -3196,7 +3091,6 @@ files = [ name = "jaraco-classes" version = "3.3.0" description = "Utility functions for Python class constructs" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3215,7 +3109,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "jcloud" version = "0.2.16" description = "Simplify deploying and managing Jina projects on Jina Cloud" -category = "main" optional = false python-versions = "*" files = [ @@ -3238,7 +3131,6 @@ test = ["black (==22.3.0)", "jina (>=3.7.0)", "mock", "pytest", "pytest-asyncio" name = "jedi" version = "0.19.0" description = "An autocompletion tool for Python that can be used for text editors." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3258,7 +3150,6 @@ testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jeepney" version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3274,7 +3165,6 @@ trio = ["async_generator", "trio"] name = "jina" version = "3.15.2" description = "Build multimodal AI services via cloud native technologies · Neural Search · Generative AI · MLOps" -category = "main" optional = false python-versions = "*" files = [ @@ -3392,7 +3282,6 @@ websockets = ["websockets"] name = "jina-hubble-sdk" version = "0.39.0" description = "SDK for Hubble API at Jina AI." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -3418,8 +3307,7 @@ full = ["aiohttp", "black (==22.3.0)", "docker", "filelock", "flake8 (==4.0.1)", name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, @@ -3436,7 +3324,6 @@ i18n = ["Babel (>=2.7)"] name = "joblib" version = "1.3.2" description = "Lightweight pipelining with Python functions" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3448,7 +3335,6 @@ files = [ name = "jupyter-client" version = "8.3.0" description = "Jupyter protocol implementation and client libraries" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3458,7 +3344,7 @@ files = [ [package.dependencies] importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" tornado = ">=6.2" @@ -3472,7 +3358,6 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt name = "jupyter-core" version = "5.3.1" description = "Jupyter core package. A base package on which Jupyter projects rely." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3493,7 +3378,6 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] name = "keyring" version = "24.2.0" description = "Store and access your passwords safely." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3550,7 +3434,6 @@ zookeeper = ["kazoo (>=2.8.0)"] name = "langchain" version = "0.0.256" description = "Building applications with LLMs through composability" -category = "main" optional = false python-versions = ">=3.8.1,<4.0" files = [ @@ -3590,7 +3473,6 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"] name = "langchain-experimental" version = "0.0.8" description = "Building applications with LLMs through composability" -category = "main" optional = false python-versions = ">=3.8.1,<4.0" files = [ @@ -3605,7 +3487,6 @@ langchain = ">=0.0.239" name = "langchain-serve" version = "0.0.60" description = "Langchain Serve - serve your langchain apps on Jina AI Cloud." -category = "main" optional = true python-versions = "*" files = [ @@ -3629,14 +3510,13 @@ test = ["psutil", "pytest", "pytest-asyncio"] [[package]] name = "langsmith" -version = "0.0.23" +version = "0.0.24" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." -category = "main" optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langsmith-0.0.23-py3-none-any.whl", hash = "sha256:68a48ecd5b389d14de55e9b6ac237a280ed3720814907277e25026b18e037103"}, - {file = "langsmith-0.0.23.tar.gz", hash = "sha256:2d26d78bef5ab928da632d0dfbae8985afa6ffdaec5a895dfa277ed24c6936fd"}, + {file = "langsmith-0.0.24-py3-none-any.whl", hash = "sha256:f9f951d070aa1919123d700642aca9c781edfc8797a65ab1161aa12f89bed707"}, + {file = "langsmith-0.0.24.tar.gz", hash = "sha256:9c066dd915752324490a735692997b0db0958f5dfc1e0a0dfbf752c6e62c7529"}, ] [package.dependencies] @@ -3647,7 +3527,6 @@ requests = ">=2,<3" name = "linkify-it-py" version = "2.0.2" description = "Links recognition library with FULL unicode support." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3666,22 +3545,21 @@ test = ["coverage", "pytest", "pytest-cov"] [[package]] name = "llama-cpp-python" -version = "0.1.68" +version = "0.1.77" description = "A Python wrapper for llama.cpp" -category = "main" optional = true python-versions = ">=3.7" files = [ - {file = "llama_cpp_python-0.1.68.tar.gz", hash = "sha256:619ca317d771fc0c30ceba68c29c318287cd1cae2eaa14661aec675190295f19"}, + {file = "llama_cpp_python-0.1.77.tar.gz", hash = "sha256:76c7fae8f5386edecf38cb149bf119127e1208883f0456c6998465648d6c242e"}, ] [package.dependencies] -diskcache = ">=5.6.1,<6.0.0" -numpy = ">=1.24.4,<2.0.0" -typing-extensions = ">=4.7.1,<5.0.0" +diskcache = ">=5.6.1" +numpy = ">=1.20.0" +typing-extensions = ">=4.5.0" [package.extras] -server = ["fastapi (>=0.99.1,<0.100.0)", "sse-starlette (>=1.6.1,<2.0.0)", "uvicorn (>=0.22.0,<0.23.0)"] +server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (>=1.6.1)", "uvicorn (>=0.22.0)"] [[package]] name = "locust" @@ -3714,7 +3592,6 @@ Werkzeug = ">=2.0.0" name = "loguru" version = "0.7.0" description = "Python logging made (stupidly) simple" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -3733,7 +3610,6 @@ dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegu name = "lxml" version = "4.9.3" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ @@ -3744,7 +3620,6 @@ files = [ {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, @@ -3753,7 +3628,6 @@ files = [ {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, @@ -3773,7 +3647,6 @@ files = [ {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, @@ -3783,7 +3656,6 @@ files = [ {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, @@ -3793,7 +3665,6 @@ files = [ {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, @@ -3803,7 +3674,6 @@ files = [ {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, @@ -3814,16 +3684,13 @@ files = [ {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, @@ -3839,7 +3706,6 @@ source = ["Cython (>=0.29.35)"] name = "lz4" version = "4.3.2" description = "LZ4 Bindings for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3908,7 +3774,6 @@ testing = ["pytest"] name = "markdown" version = "3.4.4" description = "Python implementation of John Gruber's Markdown." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3927,7 +3792,6 @@ testing = ["coverage", "pyyaml"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3954,8 +3818,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, @@ -4014,7 +3877,6 @@ files = [ name = "marshmallow" version = "3.20.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4035,7 +3897,6 @@ tests = ["pytest", "pytz", "simplejson"] name = "matplotlib-inline" version = "0.1.6" description = "Inline Matplotlib backend for Jupyter" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4050,7 +3911,6 @@ traitlets = "*" name = "mdit-py-plugins" version = "0.4.0" description = "Collection of plugins for markdown-it-py" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4070,7 +3930,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4082,7 +3941,6 @@ files = [ name = "metaphor-python" version = "0.1.14" description = "A Python package for the Metaphor API." -category = "main" optional = false python-versions = "*" files = [ @@ -4097,7 +3955,6 @@ requests = "*" name = "monotonic" version = "1.6" description = "An implementation of time.monotonic() for Python 2 & < 3.3" -category = "main" optional = false python-versions = "*" files = [ @@ -4109,7 +3966,6 @@ files = [ name = "more-itertools" version = "10.1.0" description = "More routines for operating on iterables, beyond itertools" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4121,7 +3977,6 @@ files = [ name = "mpmath" version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" -category = "main" optional = false python-versions = "*" files = [ @@ -4139,7 +3994,6 @@ tests = ["pytest (>=4.6)"] name = "msg-parser" version = "1.2.0" description = "This module enables reading, parsing and converting Microsoft Outlook MSG E-Mail files." -category = "main" optional = false python-versions = ">=3.4" files = [ @@ -4229,7 +4083,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4313,7 +4166,6 @@ files = [ name = "multiprocess" version = "0.70.15" description = "better multiprocessing and multithreading in Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4342,7 +4194,6 @@ dill = ">=0.3.7" name = "mypy" version = "1.5.1" description = "Optional static typing for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4389,7 +4240,6 @@ reports = ["lxml"] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -4401,7 +4251,6 @@ files = [ name = "nest-asyncio" version = "1.5.7" description = "Patch asyncio to allow nested event loops" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -4413,7 +4262,6 @@ files = [ name = "networkx" version = "3.1" description = "Python package for creating and manipulating graphs and networks" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4432,7 +4280,6 @@ test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] name = "nltk" version = "3.8.1" description = "Natural Language Toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4458,7 +4305,6 @@ twitter = ["twython"] name = "numexpr" version = "2.8.5" description = "Fast numerical expression evaluator for NumPy" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4501,7 +4347,6 @@ numpy = ">=1.13.3" name = "numpy" version = "1.25.2" description = "Fundamental package for array computing in Python" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -4536,7 +4381,6 @@ files = [ name = "olefile" version = "0.46" description = "Python package to parse, read and write Microsoft OLE2 files (Structured Storage or Compound Document, Microsoft Office)" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -4547,7 +4391,6 @@ files = [ name = "onnxruntime" version = "1.15.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" -category = "main" optional = false python-versions = "*" files = [ @@ -4589,7 +4432,6 @@ sympy = "*" name = "openai" version = "0.27.8" description = "Python client library for the OpenAI API" -category = "main" optional = false python-versions = ">=3.7.1" files = [ @@ -4604,7 +4446,7 @@ tqdm = "*" [package.extras] datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] +dev = ["black (>=21.6b0,<22.0)", "pytest (==6.*)", "pytest-asyncio", "pytest-mock"] embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] @@ -4612,7 +4454,6 @@ wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1 name = "openapi-schema-pydantic" version = "1.2.4" description = "OpenAPI (v3) specification schema as pydantic class" -category = "main" optional = false python-versions = ">=3.6.1" files = [ @@ -4627,7 +4468,6 @@ pydantic = ">=1.8.2" name = "openpyxl" version = "3.1.2" description = "A Python library to read/write Excel 2010 xlsx/xlsm files" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4642,7 +4482,6 @@ et-xmlfile = "*" name = "opentelemetry-api" version = "1.19.0" description = "OpenTelemetry Python API" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4658,7 +4497,6 @@ importlib-metadata = ">=6.0,<7.0" name = "opentelemetry-exporter-otlp" version = "1.19.0" description = "OpenTelemetry Collector Exporters" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4674,7 +4512,6 @@ opentelemetry-exporter-otlp-proto-http = "1.19.0" name = "opentelemetry-exporter-otlp-proto-common" version = "1.19.0" description = "OpenTelemetry Protobuf encoding" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4689,7 +4526,6 @@ opentelemetry-proto = "1.19.0" name = "opentelemetry-exporter-otlp-proto-grpc" version = "1.19.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4714,7 +4550,6 @@ test = ["pytest-grpc"] name = "opentelemetry-exporter-otlp-proto-http" version = "1.19.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4739,7 +4574,6 @@ test = ["responses (==0.22.0)"] name = "opentelemetry-exporter-prometheus" version = "1.12.0rc1" description = "Prometheus Metric Exporter for OpenTelemetry" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4756,7 +4590,6 @@ prometheus-client = ">=0.5.0,<1.0.0" name = "opentelemetry-instrumentation" version = "0.40b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4773,7 +4606,6 @@ wrapt = ">=1.0.0,<2.0.0" name = "opentelemetry-instrumentation-aiohttp-client" version = "0.40b0" description = "OpenTelemetry aiohttp client instrumentation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4796,7 +4628,6 @@ test = ["http-server-mock", "opentelemetry-instrumentation-aiohttp-client[instru name = "opentelemetry-instrumentation-asgi" version = "0.40b0" description = "ASGI instrumentation for OpenTelemetry" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4819,7 +4650,6 @@ test = ["opentelemetry-instrumentation-asgi[instruments]", "opentelemetry-test-u name = "opentelemetry-instrumentation-fastapi" version = "0.40b0" description = "OpenTelemetry FastAPI Instrumentation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4842,7 +4672,6 @@ test = ["httpx (>=0.22,<1.0)", "opentelemetry-instrumentation-fastapi[instrument name = "opentelemetry-instrumentation-grpc" version = "0.40b0" description = "OpenTelemetry gRPC instrumentation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4865,7 +4694,6 @@ test = ["opentelemetry-instrumentation-grpc[instruments]", "opentelemetry-sdk (> name = "opentelemetry-proto" version = "1.19.0" description = "OpenTelemetry Python Proto" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4880,7 +4708,6 @@ protobuf = ">=3.19,<5.0" name = "opentelemetry-sdk" version = "1.19.0" description = "OpenTelemetry Python SDK" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4897,7 +4724,6 @@ typing-extensions = ">=3.7.4" name = "opentelemetry-semantic-conventions" version = "0.40b0" description = "OpenTelemetry Semantic Conventions" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4909,7 +4735,6 @@ files = [ name = "opentelemetry-util-http" version = "0.40b0" description = "Web util for OpenTelemetry" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4921,7 +4746,6 @@ files = [ name = "orjson" version = "3.9.3" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4987,7 +4811,6 @@ files = [ name = "overrides" version = "7.4.0" description = "A decorator to automatically detect mismatch when overriding a method." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4999,7 +4822,6 @@ files = [ name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5011,7 +4833,6 @@ files = [ name = "pandas" version = "2.0.3" description = "Powerful data structures for data analysis, time series, and statistics" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -5078,7 +4899,6 @@ xml = ["lxml (>=4.6.3)"] name = "pandas-stubs" version = "2.0.3.230814" description = "Type annotations for pandas" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -5094,7 +4914,6 @@ types-pytz = ">=2022.1.1" name = "parso" version = "0.8.3" description = "A Python Parser" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -5127,7 +4946,6 @@ totp = ["cryptography"] name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5139,7 +4957,6 @@ files = [ name = "pdf2image" version = "1.16.3" description = "A wrapper around the pdftoppm and pdftocairo command line tools to convert PDF to a PIL Image list." -category = "main" optional = false python-versions = "*" files = [ @@ -5154,7 +4971,6 @@ pillow = "*" name = "pdfminer-six" version = "20221105" description = "PDF parser and analyzer" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -5175,7 +4991,6 @@ image = ["Pillow"] name = "pexpect" version = "4.8.0" description = "Pexpect allows easy control of interactive console applications." -category = "dev" optional = false python-versions = "*" files = [ @@ -5190,7 +5005,6 @@ ptyprocess = ">=0.5" name = "pickleshare" version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" -category = "dev" optional = false python-versions = "*" files = [ @@ -5202,7 +5016,6 @@ files = [ name = "pillow" version = "10.0.0" description = "Python Imaging Library (Fork)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -5270,7 +5083,6 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "pinecone-client" version = "2.2.2" description = "Pinecone client and SDK" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -5296,7 +5108,6 @@ grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv name = "pkginfo" version = "1.9.6" description = "Query metadata from sdists / bdists / installed packages." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -5311,7 +5122,6 @@ testing = ["pytest", "pytest-cov"] name = "platformdirs" version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -5327,7 +5137,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.2.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -5343,7 +5152,6 @@ testing = ["pytest", "pytest-benchmark"] name = "portalocker" version = "2.7.0" description = "Wraps the portalocker recipe for easy usage" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -5363,7 +5171,6 @@ tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "p name = "postgrest" version = "0.10.6" description = "PostgREST client for Python. This library provides an ORM interface to PostgREST." -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -5381,7 +5188,6 @@ strenum = ">=0.4.9,<0.5.0" name = "posthog" version = "3.0.1" description = "Integrate PostHog into any python application." -category = "main" optional = false python-versions = "*" files = [ @@ -5405,7 +5211,6 @@ test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint" name = "prometheus-client" version = "0.17.1" description = "Python client for the Prometheus monitoring system." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -5420,7 +5225,6 @@ twisted = ["twisted"] name = "prompt-toolkit" version = "3.0.39" description = "Library for building powerful interactive command lines in Python" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -5435,7 +5239,6 @@ wcwidth = "*" name = "proto-plus" version = "1.22.3" description = "Beautiful, Pythonic protocol buffers." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -5453,7 +5256,6 @@ testing = ["google-api-core[grpc] (>=1.31.5)"] name = "protobuf" version = "3.20.3" description = "Protocol Buffers" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5485,7 +5287,6 @@ files = [ name = "psutil" version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -5512,7 +5313,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "psycopg" version = "3.1.10" description = "PostgreSQL database adapter for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5536,7 +5336,6 @@ test = ["anyio (>=3.6.2)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5) name = "psycopg-binary" version = "3.1.10" description = "PostgreSQL database adapter for Python -- C optimisation distribution" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5600,7 +5399,6 @@ files = [ name = "psycopg2-binary" version = "2.9.7" description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -5670,7 +5468,6 @@ files = [ name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" -category = "dev" optional = false python-versions = "*" files = [ @@ -5682,7 +5479,6 @@ files = [ name = "pulsar-client" version = "3.2.0" description = "Apache Pulsar Python client library" -category = "main" optional = false python-versions = "*" files = [ @@ -5730,7 +5526,6 @@ functions = ["apache-bookkeeper-client (>=4.16.1)", "grpcio (>=1.8.2)", "prometh name = "pure-eval" version = "0.2.2" description = "Safely evaluate AST nodes without side effects" -category = "dev" optional = false python-versions = "*" files = [ @@ -5745,7 +5540,6 @@ tests = ["pytest"] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" -category = "main" optional = true python-versions = "*" files = [ @@ -5757,7 +5551,6 @@ files = [ name = "pyarrow" version = "12.0.1" description = "Python library for Apache Arrow" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5795,7 +5588,6 @@ numpy = ">=1.16.6" name = "pyasn1" version = "0.5.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -5807,7 +5599,6 @@ files = [ name = "pyasn1-modules" version = "0.3.0" description = "A collection of ASN.1-based protocols modules" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -5822,7 +5613,6 @@ pyasn1 = ">=0.4.6,<0.6.0" name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -5834,7 +5624,6 @@ files = [ name = "pydantic" version = "1.10.12" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5887,7 +5676,6 @@ email = ["email-validator (>=1.0.3)"] name = "pygments" version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5902,7 +5690,6 @@ plugins = ["importlib-metadata"] name = "pymongo" version = "4.4.1" description = "Python driver for MongoDB " -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -5997,7 +5784,6 @@ zstd = ["zstandard"] name = "pypandoc" version = "1.11" description = "Thin wrapper for pandoc." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -6009,7 +5795,6 @@ files = [ name = "pyparsing" version = "3.1.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" optional = false python-versions = ">=3.6.8" files = [ @@ -6024,7 +5809,6 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pypdf" version = "3.15.1" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -6046,7 +5830,6 @@ image = ["Pillow (>=8.0.0)"] name = "pyreadline3" version = "3.4.1" description = "A python implementation of GNU readline." -category = "main" optional = false python-versions = "*" files = [ @@ -6058,7 +5841,6 @@ files = [ name = "pysrt" version = "1.1.2" description = "SubRip (.srt) subtitle parser and writer" -category = "main" optional = false python-versions = "*" files = [ @@ -6072,7 +5854,6 @@ chardet = "*" name = "pytest" version = "7.4.0" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -6095,7 +5876,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -6114,7 +5894,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -6129,7 +5908,6 @@ six = ">=1.5" name = "python-docx" version = "0.8.11" description = "Create and update Microsoft Word .docx files." -category = "main" optional = false python-versions = "*" files = [ @@ -6143,7 +5921,6 @@ lxml = ">=2.3.2" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -6158,7 +5935,6 @@ cli = ["click (>=5.0)"] name = "python-gitlab" version = "3.15.0" description = "Interact with GitLab API" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -6178,7 +5954,6 @@ yaml = ["PyYaml (>=5.2)"] name = "python-jose" version = "3.3.0" description = "JOSE implementation in Python" -category = "main" optional = false python-versions = "*" files = [ @@ -6200,7 +5975,6 @@ pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] name = "python-magic" version = "0.4.27" description = "File type identification using libmagic" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -6212,7 +5986,6 @@ files = [ name = "python-multipart" version = "0.0.6" description = "A streaming multipart parser for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -6227,7 +6000,6 @@ dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatc name = "python-pptx" version = "0.6.21" description = "Generate and manipulate Open XML PowerPoint (.pptx) files" -category = "main" optional = false python-versions = "*" files = [ @@ -6243,7 +6015,6 @@ XlsxWriter = ">=0.5.7" name = "python-semantic-release" version = "7.33.2" description = "Automatic Semantic Versioning for Python projects" -category = "main" optional = false python-versions = "*" files = [ @@ -6275,7 +6046,6 @@ test = ["coverage (>=5,<6)", "mock (==1.3.0)", "pytest (>=7,<8)", "pytest-mock ( name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -6287,7 +6057,6 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" -category = "main" optional = false python-versions = "*" files = [ @@ -6311,7 +6080,6 @@ files = [ name = "pywin32-ctypes" version = "0.2.2" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -6323,7 +6091,6 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -6373,7 +6140,6 @@ files = [ name = "pyzmq" version = "25.1.1" description = "Python bindings for 0MQ" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -6479,7 +6245,6 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "qdrant-client" version = "1.4.0" description = "Client library for the Qdrant vector search engine" -category = "main" optional = false python-versions = ">=3.7,<3.12" files = [ @@ -6500,7 +6265,6 @@ urllib3 = ">=1.26.14,<2.0.0" name = "readme-renderer" version = "40.0" description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -6520,7 +6284,6 @@ md = ["cmarkgfm (>=0.8.0)"] name = "realtime" version = "1.0.0" description = "" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -6555,7 +6318,6 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" name = "regex" version = "2023.8.8" description = "Alternative regular expression module, to replace re." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -6653,7 +6415,6 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -6675,7 +6436,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -6690,7 +6450,6 @@ requests = ">=2.0.1,<3.0.0" name = "rfc3986" version = "1.5.0" description = "Validating URI References per RFC 3986" -category = "main" optional = false python-versions = "*" files = [ @@ -6708,7 +6467,6 @@ idna2008 = ["idna"] name = "rich" version = "13.5.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -6737,7 +6495,6 @@ files = [ name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" -category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -6752,7 +6509,6 @@ pyasn1 = ">=0.1.3" name = "ruff" version = "0.0.254" description = "An extremely fast Python linter, written in Rust." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -6779,48 +6535,34 @@ files = [ name = "safetensors" version = "0.3.2" description = "Fast and Safe Tensor serialization" -category = "main" optional = true python-versions = "*" files = [ {file = "safetensors-0.3.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b6a66989075c2891d743153e8ba9ca84ee7232c8539704488f454199b8b8f84d"}, {file = "safetensors-0.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:670d6bc3a3b377278ce2971fa7c36ebc0a35041c4ea23b9df750a39380800195"}, - {file = "safetensors-0.3.2-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:564f42838721925b5313ae864ba6caa6f4c80a9fbe63cf24310c3be98ab013cd"}, {file = "safetensors-0.3.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:7f80af7e4ab3188daaff12d43d078da3017a90d732d38d7af4eb08b6ca2198a5"}, - {file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec30d78f20f1235b252d59cbb9755beb35a1fde8c24c89b3c98e6a1804cfd432"}, - {file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16063d94d8f600768d3c331b1e97964b1bf3772e19710105fe24ec5a6af63770"}, {file = "safetensors-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb44e140bf2aeda98d9dde669dbec15f7b77f96a9274469b91a6cf4bcc5ec3b"}, {file = "safetensors-0.3.2-cp310-cp310-win32.whl", hash = "sha256:2961c1243fd0da46aa6a1c835305cc4595486f8ac64632a604d0eb5f2de76175"}, {file = "safetensors-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c813920482c337d1424d306e1b05824a38e3ef94303748a0a287dea7a8c4f805"}, {file = "safetensors-0.3.2-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:707df34bd9b9047e97332136ad98e57028faeccdb9cfe1c3b52aba5964cc24bf"}, {file = "safetensors-0.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:becc5bb85b2947eae20ed23b407ebfd5277d9a560f90381fe2c42e6c043677ba"}, - {file = "safetensors-0.3.2-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:30a75707be5cc9686490bde14b9a371cede4af53244ea72b340cfbabfffdf58a"}, {file = "safetensors-0.3.2-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:54ad6af663e15e2b99e2ea3280981b7514485df72ba6d014dc22dae7ba6a5e6c"}, - {file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37764b3197656ef507a266c453e909a3477dabc795962b38e3ad28226f53153b"}, - {file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4939067736783acd8391d83cd97d6c202f94181951ce697d519f9746381b6a39"}, {file = "safetensors-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0fac127ff8fb04834da5c6d85a8077e6a1c9180a11251d96f8068db922a17"}, {file = "safetensors-0.3.2-cp311-cp311-win32.whl", hash = "sha256:155b82dbe2b0ebff18cde3f76b42b6d9470296e92561ef1a282004d449fa2b4c"}, {file = "safetensors-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:a86428d196959619ce90197731be9391b5098b35100a7228ef4643957648f7f5"}, {file = "safetensors-0.3.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:c1f8ab41ed735c5b581f451fd15d9602ff51aa88044bfa933c5fa4b1d0c644d1"}, {file = "safetensors-0.3.2-cp37-cp37m-macosx_13_0_x86_64.whl", hash = "sha256:bc9cfb3c9ea2aec89685b4d656f9f2296f0f0d67ecf2bebf950870e3be89b3db"}, - {file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ace5d471e3d78e0d93f952707d808b5ab5eac77ddb034ceb702e602e9acf2be9"}, - {file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de3e20a388b444381bcda1a3193cce51825ddca277e4cf3ed1fe8d9b2d5722cd"}, {file = "safetensors-0.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d7d70d48585fe8df00725aa788f2e64fd24a4c9ae07cd6be34f6859d0f89a9c"}, {file = "safetensors-0.3.2-cp37-cp37m-win32.whl", hash = "sha256:6ff59bc90cdc857f68b1023be9085fda6202bbe7f2fd67d06af8f976d6adcc10"}, {file = "safetensors-0.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8b05c93da15fa911763a89281906ca333ed800ab0ef1c7ce53317aa1a2322f19"}, {file = "safetensors-0.3.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:8969cfd9e8d904e8d3c67c989e1bd9a95e3cc8980d4f95e4dcd43c299bb94253"}, {file = "safetensors-0.3.2-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:f54148ac027556eb02187e9bc1556c4d916c99ca3cb34ca36a7d304d675035c1"}, - {file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caec25fedbcf73f66c9261984f07885680f71417fc173f52279276c7f8a5edd3"}, - {file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50224a1d99927ccf3b75e27c3d412f7043280431ab100b4f08aad470c37cf99a"}, {file = "safetensors-0.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa98f49e95f02eb750d32c4947e7d5aa43883149ebd0414920866446525b70f0"}, {file = "safetensors-0.3.2-cp38-cp38-win32.whl", hash = "sha256:33409df5e28a83dc5cc5547a3ac17c0f1b13a1847b1eb3bc4b3be0df9915171e"}, {file = "safetensors-0.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:e04a7cbbb3856159ab99e3adb14521544f65fcb8548cce773a1435a0f8d78d27"}, {file = "safetensors-0.3.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:7c864cf5dcbfb608c5378f83319c60cc9c97263343b57c02756b7613cd5ab4dd"}, {file = "safetensors-0.3.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:14e8c19d6dc51d4f70ee33c46aff04c8ba3f95812e74daf8036c24bc86e75cae"}, - {file = "safetensors-0.3.2-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:042a60f633c3c7009fdf6a7c182b165cb7283649d2a1e9c7a4a1c23454bd9a5b"}, {file = "safetensors-0.3.2-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:fafd95e5ef41e8f312e2a32b7031f7b9b2a621b255f867b221f94bb2e9f51ae8"}, - {file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ed77cf358abce2307f03634694e0b2a29822e322a1623e0b1aa4b41e871bf8b"}, - {file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d344e8b2681a33aafc197c90b0def3229b3317d749531c72fa6259d0caa5c8c"}, {file = "safetensors-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87ff0024ef2e5722a79af24688ce4a430f70601d0cf712a744105ed4b8f67ba5"}, {file = "safetensors-0.3.2-cp39-cp39-win32.whl", hash = "sha256:827af9478b78977248ba93e2fd97ea307fb63f463f80cef4824460f8c2542a52"}, {file = "safetensors-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9b09f27c456efa301f98681ea14b12f81f2637889f6336223ccab71e42c34541"}, @@ -6843,7 +6585,6 @@ torch = ["torch (>=1.10)"] name = "scikit-learn" version = "1.3.0" description = "A set of python modules for machine learning and data mining" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -6886,7 +6627,6 @@ tests = ["black (>=23.3.0)", "matplotlib (>=3.1.3)", "mypy (>=1.3)", "numpydoc ( name = "scipy" version = "1.11.1" description = "Fundamental algorithms for scientific computing in Python" -category = "main" optional = true python-versions = "<3.13,>=3.9" files = [ @@ -6923,7 +6663,6 @@ test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeo name = "secretstorage" version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -6939,7 +6678,6 @@ jeepney = ">=0.6" name = "semver" version = "2.13.0" description = "Python helper for Semantic Versioning (http://semver.org/)" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -6951,7 +6689,6 @@ files = [ name = "sentence-transformers" version = "2.2.2" description = "Multilingual text embeddings" -category = "main" optional = true python-versions = ">=3.6.0" files = [ @@ -6974,7 +6711,6 @@ transformers = ">=4.6.0,<5.0.0" name = "sentencepiece" version = "0.1.99" description = "SentencePiece python wrapper" -category = "main" optional = true python-versions = "*" files = [ @@ -7029,7 +6765,6 @@ files = [ name = "setuptools" version = "68.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -7046,7 +6781,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "shapely" version = "1.8.5.post1" description = "Geometric objects, predicates, and operations" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -7103,7 +6837,6 @@ vectorized = ["numpy"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -7115,7 +6848,6 @@ files = [ name = "slack-bolt" version = "1.18.0" description = "The Bolt Framework for Python" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -7137,7 +6869,6 @@ testing-without-asyncio = ["Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "Werk name = "slack-sdk" version = "3.21.3" description = "The Slack API Platform SDK for Python" -category = "main" optional = true python-versions = ">=3.6.0" files = [ @@ -7153,7 +6884,6 @@ testing = ["Flask (>=1,<2)", "Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "We name = "smmap" version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -7165,7 +6895,6 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7177,7 +6906,6 @@ files = [ name = "soupsieve" version = "2.4.1" description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7189,7 +6917,6 @@ files = [ name = "sqlalchemy" version = "1.4.41" description = "Database Abstraction Library" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -7237,7 +6964,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"} [package.extras] aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] @@ -7264,7 +6991,6 @@ sqlcipher = ["sqlcipher3-binary"] name = "sqlalchemy2-stubs" version = "0.0.2a35" description = "Typing Stubs for SQLAlchemy 1.4" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -7279,7 +7005,6 @@ typing-extensions = ">=3.7.4" name = "sqlmodel" version = "0.0.8" description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." -category = "main" optional = false python-versions = ">=3.6.1,<4.0.0" files = [ @@ -7296,7 +7021,6 @@ sqlalchemy2-stubs = "*" name = "stack-data" version = "0.6.2" description = "Extract data from python stack frames and tracebacks for informative displays" -category = "dev" optional = false python-versions = "*" files = [ @@ -7316,7 +7040,6 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "starlette" version = "0.27.0" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7335,7 +7058,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam name = "storage3" version = "0.5.3" description = "Supabase Storage client for Python." -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -7352,7 +7074,6 @@ typing-extensions = ">=4.2.0,<5.0.0" name = "strenum" version = "0.4.15" description = "An Enum that inherits from str." -category = "main" optional = false python-versions = "*" files = [ @@ -7369,7 +7090,6 @@ test = ["pylint", "pytest", "pytest-black", "pytest-cov", "pytest-pylint"] name = "supabase" version = "1.0.3" description = "Supabase client for Python." -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -7390,7 +7110,6 @@ supafunc = ">=0.2.2,<0.3.0" name = "supafunc" version = "0.2.2" description = "Library for Supabase Functions" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -7405,7 +7124,6 @@ httpx = ">=0.23.0,<0.24.0" name = "sympy" version = "1.12" description = "Computer algebra system (CAS) in Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -7420,7 +7138,6 @@ mpmath = ">=0.19" name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7435,7 +7152,6 @@ widechars = ["wcwidth"] name = "tenacity" version = "8.2.3" description = "Retry code until it succeeds" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7450,7 +7166,6 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"] name = "textual" version = "0.33.0" description = "Modern Text User Interface framework" -category = "main" optional = true python-versions = ">=3.7,<4.0" files = [ @@ -7468,7 +7183,6 @@ typing-extensions = ">=4.4.0,<5.0.0" name = "threadpoolctl" version = "3.2.0" description = "threadpoolctl" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -7480,7 +7194,6 @@ files = [ name = "tiktoken" version = "0.4.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -7526,7 +7239,6 @@ blobfile = ["blobfile (>=2)"] name = "tokenizers" version = "0.13.3" description = "Fast and Customizable Tokenizers" -category = "main" optional = false python-versions = "*" files = [ @@ -7581,7 +7293,6 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" optional = true python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -7593,7 +7304,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -7605,7 +7315,6 @@ files = [ name = "tomlkit" version = "0.12.1" description = "Style preserving TOML library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7617,7 +7326,6 @@ files = [ name = "torch" version = "2.0.1" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" -category = "main" optional = true python-versions = ">=3.8.0" files = [ @@ -7657,7 +7365,6 @@ opt-einsum = ["opt-einsum (>=3.3)"] name = "torchvision" version = "0.15.2" description = "image and video datasets and models for torch deep learning" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -7685,7 +7392,7 @@ files = [ [package.dependencies] numpy = "*" -pillow = ">=5.3.0,<8.3.0 || >=8.4.0" +pillow = ">=5.3.0,<8.3.dev0 || >=8.4.dev0" requests = "*" torch = "2.0.1" @@ -7696,7 +7403,6 @@ scipy = ["scipy"] name = "tornado" version = "6.3.3" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" optional = false python-versions = ">= 3.8" files = [ @@ -7717,7 +7423,6 @@ files = [ name = "tqdm" version = "4.66.1" description = "Fast, Extensible Progress Meter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -7738,7 +7443,6 @@ telegram = ["requests"] name = "traitlets" version = "5.9.0" description = "Traitlets Python configuration system" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -7754,7 +7458,6 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] name = "transformers" version = "4.31.0" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" -category = "main" optional = true python-versions = ">=3.8.0" files = [ @@ -7824,7 +7527,6 @@ vision = ["Pillow (<10.0.0)"] name = "twine" version = "3.8.0" description = "Collection of utilities for publishing packages on PyPI" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -7848,7 +7550,6 @@ urllib3 = ">=1.26.0" name = "typer" version = "0.9.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -7870,7 +7571,6 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. name = "types-appdirs" version = "1.4.3.5" description = "Typing stubs for appdirs" -category = "dev" optional = false python-versions = "*" files = [ @@ -7882,7 +7582,6 @@ files = [ name = "types-cachetools" version = "5.3.0.6" description = "Typing stubs for cachetools" -category = "main" optional = false python-versions = "*" files = [ @@ -7905,7 +7604,6 @@ files = [ name = "types-pillow" version = "9.5.0.6" description = "Typing stubs for Pillow" -category = "dev" optional = false python-versions = "*" files = [ @@ -7942,7 +7640,6 @@ types-pyasn1 = "*" name = "types-pytz" version = "2023.3.0.1" description = "Typing stubs for pytz" -category = "dev" optional = false python-versions = "*" files = [ @@ -7954,7 +7651,6 @@ files = [ name = "types-pyyaml" version = "6.0.12.11" description = "Typing stubs for PyYAML" -category = "dev" optional = false python-versions = "*" files = [ @@ -7966,7 +7662,6 @@ files = [ name = "types-requests" version = "2.31.0.2" description = "Typing stubs for requests" -category = "dev" optional = false python-versions = "*" files = [ @@ -7981,7 +7676,6 @@ types-urllib3 = "*" name = "types-urllib3" version = "1.26.25.14" description = "Typing stubs for urllib3" -category = "dev" optional = false python-versions = "*" files = [ @@ -7993,7 +7687,6 @@ files = [ name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8005,7 +7698,6 @@ files = [ name = "typing-inspect" version = "0.9.0" description = "Runtime inspection utilities for typing module." -category = "main" optional = false python-versions = "*" files = [ @@ -8021,7 +7713,6 @@ typing-extensions = ">=3.7.4" name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -8033,7 +7724,6 @@ files = [ name = "uc-micro-py" version = "1.0.2" description = "Micro subset of unicode data files for linkify-it-py projects." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -8048,7 +7738,6 @@ test = ["coverage", "pytest", "pytest-cov"] name = "unstructured" version = "0.7.12" description = "A library that prepares raw documents for downstream ML tasks." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -8096,7 +7785,6 @@ wikipedia = ["wikipedia"] name = "uritemplate" version = "4.1.1" description = "Implementation of RFC 6570 URI Templates" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -8108,7 +7796,6 @@ files = [ name = "urllib3" version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -8125,7 +7812,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "uvicorn" version = "0.22.0" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8140,7 +7826,7 @@ h11 = ">=0.8" httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} @@ -8151,7 +7837,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "uvloop" version = "0.17.0" description = "Fast implementation of asyncio event loop on top of libuv" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8196,7 +7881,6 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my name = "validators" version = "0.21.0" description = "Python Data Validation for Humans™" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -8219,7 +7903,6 @@ files = [ name = "watchfiles" version = "0.19.0" description = "Simple, modern and high performance file watching and code reload in python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8254,7 +7937,6 @@ anyio = ">=3.0.0" name = "wcwidth" version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" optional = false python-versions = "*" files = [ @@ -8266,7 +7948,6 @@ files = [ name = "weaviate-client" version = "3.22.1" description = "A python native Weaviate client" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -8287,7 +7968,6 @@ grpc = ["grpcio", "grpcio-tools"] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" -category = "main" optional = false python-versions = "*" files = [ @@ -8299,7 +7979,6 @@ files = [ name = "websocket-client" version = "1.6.1" description = "WebSocket client for Python with low level API options" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8316,7 +7995,6 @@ test = ["websockets"] name = "websockets" version = "10.4" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8412,7 +8090,6 @@ watchdog = ["watchdog (>=2.3)"] name = "wheel" version = "0.41.1" description = "A built-package format for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8427,7 +8104,6 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] name = "wikipedia" version = "1.4.0" description = "Wikipedia API for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -8442,7 +8118,6 @@ requests = ">=2.0.0,<3.0.0" name = "win32-setctime" version = "1.1.0" description = "A small Python utility to set file creation time on Windows" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -8457,7 +8132,6 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -8542,7 +8216,6 @@ files = [ name = "xlrd" version = "2.0.1" description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -8559,7 +8232,6 @@ test = ["pytest", "pytest-cov"] name = "xlsxwriter" version = "3.1.2" description = "A Python module for creating Excel XLSX files." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -8571,7 +8243,6 @@ files = [ name = "yarl" version = "1.9.2" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8659,7 +8330,6 @@ multidict = ">=4.0" name = "zipp" version = "3.16.2" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -8740,7 +8410,6 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] name = "zstandard" version = "0.21.0" description = "Zstandard bindings for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -8797,8 +8466,8 @@ cffi = ["cffi (>=1.11)"] [extras] all = [] -deploy = ["langchain-serve"] -local = ["llama-cpp-python", "sentence-transformers", "ctransformers"] +deploy = ["celery", "flower", "langchain-serve", "redis"] +local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" From 39e25c294c3458322bedc3227ed7f6c9e0167250 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 10:59:54 -0300 Subject: [PATCH 044/213] Merge dev --- src/frontend/src/App.tsx | 16 +++-- .../EditFlowSettingsComponent/index.tsx | 22 +++++-- .../src/components/inputComponent/index.tsx | 7 +++ src/frontend/src/constants/constants.ts | 3 + src/frontend/src/contexts/typesContext.tsx | 8 ++- src/frontend/src/modals/exportModal/index.tsx | 29 +++++---- .../src/modals/flowSettingsModal/index.tsx | 19 +++--- .../src/modals/genericModal/index.tsx | 1 + .../components/PageComponent/index.tsx | 12 +++- src/frontend/src/pages/FlowPage/index.tsx | 34 ++++++----- src/frontend/src/pages/MainPage/index.tsx | 61 +++++++++---------- src/frontend/src/routes.tsx | 1 + src/frontend/src/types/typesContext/index.ts | 2 + src/frontend/src/utils/reactflowUtils.ts | 1 + 14 files changed, 130 insertions(+), 86 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 46a2e89d2..467e15ed8 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -9,6 +9,7 @@ import ErrorAlert from "./alerts/error"; import NoticeAlert from "./alerts/notice"; import SuccessAlert from "./alerts/success"; import CrashErrorComponent from "./components/CrashErrorComponent"; +import LoadingComponent from "./components/loadingComponent"; import { alertContext } from "./contexts/alertContext"; import { locationContext } from "./contexts/locationContext"; import { TabsContext } from "./contexts/tabsContext"; @@ -47,10 +48,6 @@ export default function App() { }> >([]); - const isLoginPage = location.pathname.includes("login"); - const isAdminPage = location.pathname.includes("admin"); - const isSignUpPage = location.pathname.includes("signup"); - // Use effect hook to update alertsList when a new alert is added useEffect(() => { // If there is an error alert open with data, add it to the alertsList @@ -138,8 +135,15 @@ export default function App() { }} FallbackComponent={CrashErrorComponent} > - {!isLoginPage && !isSignUpPage &&
} - + {loading ? ( +
+ +
+ ) : ( + <> + + + )}
diff --git a/src/frontend/src/components/EditFlowSettingsComponent/index.tsx b/src/frontend/src/components/EditFlowSettingsComponent/index.tsx index b641be41b..77fd64819 100644 --- a/src/frontend/src/components/EditFlowSettingsComponent/index.tsx +++ b/src/frontend/src/components/EditFlowSettingsComponent/index.tsx @@ -34,6 +34,13 @@ export const EditFlowSettings: React.FC = ({ } else { setIsMaxLength(false); } + if (invalidName !== undefined) { + if (!nameLists.current.includes(value)) { + setInvalidName(false); + } else { + setInvalidName(true); + } + } if (!nameLists.current.includes(value)) { setInvalidName!(false); } else { @@ -43,13 +50,18 @@ export const EditFlowSettings: React.FC = ({ setCurrentName(value); }; - const [desc, setDesc] = useState( - flows.find((flow) => flow.id === tabId)?.description - ); + const [currentName, setCurrentName] = useState(name); + + const [currentDescription, setCurrentDescription] = useState(description); + + useEffect(() => { + setCurrentName(name); + setCurrentDescription(description); + }, [name, description]); const handleDescriptionChange = (event: ChangeEvent) => { - flows.find((flow) => flow.id === tabId)!.description = event.target.value; - setDesc(flows.find((flow) => flow.id === tabId)?.description); + flows.find((f) => f.id === tabId).description = event.target.value; + setCurrentDescription(flows.find((f) => f.id === tabId).description); setDescription(event.target.value); }; diff --git a/src/frontend/src/components/inputComponent/index.tsx b/src/frontend/src/components/inputComponent/index.tsx index 47e04811b..287ceac63 100644 --- a/src/frontend/src/components/inputComponent/index.tsx +++ b/src/frontend/src/components/inputComponent/index.tsx @@ -1,6 +1,7 @@ import * as Form from "@radix-ui/react-form"; import { useEffect, useState } from "react"; import { InputComponentType } from "../../types/components"; +import { handleKeyDown } from "../../utils/reactflowUtils"; import { classNames } from "../../utils/utils"; import { Input } from "../ui/input"; @@ -45,6 +46,9 @@ export default function InputComponent({ onChange={(e) => { onChange(e.target.value); }} + onKeyDown={(e) => { + handleKeyDown(e, value, ""); + }} /> ) : ( @@ -65,6 +69,9 @@ export default function InputComponent({ onChange={(e) => { onChange(e.target.value); }} + onKeyDown={(e) => { + handleKeyDown(e, value, ""); + }} /> )} {password && ( diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 7cc5f0d3a..7f6c7614c 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -509,6 +509,9 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [ "/api/v1/custom_component", "/api/v1/validate/prompt", ]; + +export const skipNodeUpdate = ["CustomComponent"]; + export const CONTROL_INPUT_STATE = { password: "", cnfPassword: "", diff --git a/src/frontend/src/contexts/typesContext.tsx b/src/frontend/src/contexts/typesContext.tsx index 936f6105e..52a5eea72 100644 --- a/src/frontend/src/contexts/typesContext.tsx +++ b/src/frontend/src/contexts/typesContext.tsx @@ -1,4 +1,10 @@ -import { createContext, ReactNode, useEffect, useState } from "react"; +import { + createContext, + ReactNode, + useContext, + useEffect, + useState, +} from "react"; import { Node, ReactFlowInstance } from "reactflow"; import { getAll } from "../controllers/API"; import { APIKindType } from "../types/api"; diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index a49d5e9a9..c296a1e2c 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode, forwardRef, useContext, useState } from "react"; +import { ReactNode, forwardRef, useContext, useEffect, useState } from "react"; import EditFlowSettings from "../../components/EditFlowSettingsComponent"; import IconComponent from "../../components/genericIconComponent"; import { Button } from "../../components/ui/button"; @@ -10,17 +10,17 @@ import BaseModal from "../baseModal"; const ExportModal = forwardRef( (props: { children: ReactNode }, ref): JSX.Element => { - const { flows, tabId, updateFlow, downloadFlow, saveFlow } = - useContext(TabsContext); + const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext); const [checked, setChecked] = useState(false); - const [name, setName] = useState( - flows.find((flow) => flow.id === tabId)?.name - ); - const [invalidName, setInvalidName] = useState(false); - const [description, setDescription] = useState( - flows.find((flow) => flow.id === tabId)?.description - ); + const flow = flows.find((f) => f.id === tabId); + useEffect(() => { + setName(flow.name); + setDescription(flow.description); + }, [flow.name, flow.description]); + const [name, setName] = useState(flow.name); + const [description, setDescription] = useState(flow.description); const [open, setOpen] = useState(false); + return ( {props.children} @@ -34,19 +34,18 @@ const ExportModal = forwardRef(
{ + onCheckedChange={(event: boolean) => { setChecked(event); }} /> diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index 2faa5e204..075f0a651 100644 --- a/src/frontend/src/modals/flowSettingsModal/index.tsx +++ b/src/frontend/src/modals/flowSettingsModal/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import EditFlowSettings from "../../components/EditFlowSettingsComponent"; import IconComponent from "../../components/genericIconComponent"; import { Button } from "../../components/ui/button"; @@ -12,16 +12,15 @@ export default function FlowSettingsModal({ open, setOpen, }: FlowSettingsPropsType): JSX.Element { - const { setErrorData, setSuccessData } = useContext(alertContext); - const ref = useRef(); + const { setSuccessData } = useContext(alertContext); const { flows, tabId, updateFlow, saveFlow } = useContext(TabsContext); - const maxLength = 50; - const [name, setName] = useState( - flows.find((flow) => flow.id === tabId)!.name - ); - const [description, setDescription] = useState( - flows.find((flow) => flow.id === tabId)!.description - ); + const flow = flows.find((f) => f.id === tabId); + useEffect(() => { + setName(flow.name); + setDescription(flow.description); + }, [flow.name, flow.description]); + const [name, setName] = useState(flow.name); + const [description, setDescription] = useState(flow.description); const [invalidName, setInvalidName] = useState(false); function handleClick(): void { diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx index 29c2df909..9498bc941 100644 --- a/src/frontend/src/modals/genericModal/index.tsx +++ b/src/frontend/src/modals/genericModal/index.tsx @@ -16,6 +16,7 @@ import { TypeModal } from "../../constants/enums"; import { alertContext } from "../../contexts/alertContext"; import { postValidatePrompt } from "../../controllers/API"; import { genericModalPropsType } from "../../types/components"; +import { handleKeyDown } from "../../utils/reactflowUtils"; import { classNames, getRandomKeyByssmm, diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 1f031d2ef..0a147373a 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -44,7 +44,13 @@ const nodeTypes = { genericNode: GenericNode, }; -export default function Page({ flow }: { flow: FlowType }): JSX.Element { +export default function Page({ + flow, + view, +}: { + flow: FlowType; + view?: boolean; +}): JSX.Element { let { updateFlow, uploadFlow, @@ -424,7 +430,9 @@ export default function Page({ flow }: { flow: FlowType }): JSX.Element { > )} - + {!view && ( + + )}
) : ( <> diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index c8231cfd2..e9e5c2f34 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -1,5 +1,6 @@ import { useContext, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; +import Header from "../../components/headerComponent"; import { TabsContext } from "../../contexts/tabsContext"; import { getVersion } from "../../controllers/API"; import Page from "./components/PageComponent"; @@ -22,20 +23,23 @@ export default function FlowPage(): JSX.Element { }, []); return ( -
- {flows.length > 0 && - tabId !== "" && - flows.findIndex((flow) => flow.id === tabId) !== -1 && ( - flow.id === tabId)!} /> - )} - - {version &&
⛓️ Langflow v{version}
} -
Created by Logspace
-
-
+ <> +
+
+ {flows.length > 0 && + tabId !== "" && + flows.findIndex((flow) => flow.id === tabId) !== -1 && ( + flow.id === tabId)!} /> + )} + + {version &&
⛓️ Langflow v{version}
} +
Created by Logspace
+
+
+ ); } diff --git a/src/frontend/src/pages/MainPage/index.tsx b/src/frontend/src/pages/MainPage/index.tsx index e5394280b..67412401a 100644 --- a/src/frontend/src/pages/MainPage/index.tsx +++ b/src/frontend/src/pages/MainPage/index.tsx @@ -1,5 +1,6 @@ import { useContext, useEffect } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; +import { CardComponent } from "../../components/cardComponent"; import IconComponent from "../../components/genericIconComponent"; import Header from "../../components/headerComponent"; import { Button } from "../../components/ui/button"; @@ -47,7 +48,7 @@ export default function HomePage(): JSX.Element { - - +
+ {flows.map((flow, idx) => ( + + + + } + onDelete={() => { + removeFlow(flow.id); + }} + /> + ))}
diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx index 2fd820862..0d023e360 100644 --- a/src/frontend/src/routes.tsx +++ b/src/frontend/src/routes.tsx @@ -4,6 +4,7 @@ import LoginAdminPage from "./pages/AdminPage/LoginPage"; import CommunityPage from "./pages/CommunityPage"; import FlowPage from "./pages/FlowPage"; import HomePage from "./pages/MainPage"; +import ViewPage from "./pages/ViewPage"; import DeleteAccountPage from "./pages/deleteAccountPage"; import LoginPage from "./pages/loginPage"; diff --git a/src/frontend/src/types/typesContext/index.ts b/src/frontend/src/types/typesContext/index.ts index 5c5d3daa6..9e57822b9 100644 --- a/src/frontend/src/types/typesContext/index.ts +++ b/src/frontend/src/types/typesContext/index.ts @@ -37,6 +37,8 @@ export type alertContextType = { pushNotificationList: (Object: AlertItemType) => void; clearNotificationList: () => void; removeFromNotificationList: (index: string) => void; + loading: boolean; + setLoading: (newState: boolean) => void; }; export type darkContextType = { diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index 9c4811178..13c3973f1 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -5,6 +5,7 @@ import { ReactFlowInstance, ReactFlowJsonObject, } from "reactflow"; +import { specialCharsRegex } from "../constants/constants"; import { APITemplateType } from "../types/api"; import { FlowType, NodeType } from "../types/flow"; import { cleanEdgesType } from "../types/utils/reactflowUtils"; From c49e660e80a73263e0e53db782d8cf16d59c8ef8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 17:23:31 -0300 Subject: [PATCH 045/213] =?UTF-8?q?=F0=9F=90=9B=20fix(cache/factory.py):?= =?UTF-8?q?=20change=20variable=20name=20from=20settings=5Fservice=20to=20?= =?UTF-8?q?settings=5Fmanager=20for=20consistency=20and=20clarity=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(cache/factory.py):=20change=20variable=20nam?= =?UTF-8?q?e=20from=20settings=5Fservice=20to=20settings=5Fmanager=20for?= =?UTF-8?q?=20consistency=20and=20clarity=20=F0=9F=90=9B=20fix(database/fa?= =?UTF-8?q?ctory.py):=20change=20variable=20name=20from=20settings=5Fservi?= =?UTF-8?q?ce=20to=20settings=5Fmanager=20for=20consistency=20and=20clarit?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/factory.py | 14 +++++++------- src/backend/langflow/services/chat/factory.py | 2 +- src/backend/langflow/services/database/factory.py | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/backend/langflow/services/cache/factory.py b/src/backend/langflow/services/cache/factory.py index 78e307bfa..018c0284a 100644 --- a/src/backend/langflow/services/cache/factory.py +++ b/src/backend/langflow/services/cache/factory.py @@ -7,17 +7,17 @@ class CacheManagerFactory(ServiceFactory): def __init__(self): super().__init__(BaseCacheManager) - def create(self, settings_service): + def create(self, settings_manager): # Here you would have logic to create and configure a CacheManager # based on the settings_service - if settings_service.settings.CACHE_TYPE == "redis": + if settings_manager.settings.CACHE_TYPE == "redis": logger.debug("Creating Redis cache") redis_cache = RedisCache( - host=settings_service.settings.REDIS_HOST, - port=settings_service.settings.REDIS_PORT, - db=settings_service.settings.REDIS_DB, - expiration_time=settings_service.settings.REDIS_CACHE_EXPIRE, + host=settings_manager.settings.REDIS_HOST, + port=settings_manager.settings.REDIS_PORT, + db=settings_manager.settings.REDIS_DB, + expiration_time=settings_manager.settings.REDIS_CACHE_EXPIRE, ) if redis_cache.is_connected(): logger.debug("Redis cache is connected") @@ -27,5 +27,5 @@ class CacheManagerFactory(ServiceFactory): ) return InMemoryCache() - elif settings_service.settings.CACHE_TYPE == "memory": + elif settings_manager.settings.CACHE_TYPE == "memory": return InMemoryCache() diff --git a/src/backend/langflow/services/chat/factory.py b/src/backend/langflow/services/chat/factory.py index 03597ed11..ca844893a 100644 --- a/src/backend/langflow/services/chat/factory.py +++ b/src/backend/langflow/services/chat/factory.py @@ -6,6 +6,6 @@ class ChatManagerFactory(ServiceFactory): def __init__(self): super().__init__(ChatManager) - def create(self, settings_service): + def create(self): # Here you would have logic to create and configure a ChatManager return ChatManager() diff --git a/src/backend/langflow/services/database/factory.py b/src/backend/langflow/services/database/factory.py index fecf24543..25427b7b9 100644 --- a/src/backend/langflow/services/database/factory.py +++ b/src/backend/langflow/services/database/factory.py @@ -10,8 +10,8 @@ class DatabaseManagerFactory(ServiceFactory): def __init__(self): super().__init__(DatabaseManager) - def create(self, settings_service: "SettingsManager"): + def create(self, settings_manager: "SettingsManager"): # Here you would have logic to create and configure a DatabaseManager - if not settings_service.settings.DATABASE_URL: + if not settings_manager.settings.DATABASE_URL: raise ValueError("No database URL provided") - return DatabaseManager(settings_service.settings.DATABASE_URL) + return DatabaseManager(settings_manager.settings.DATABASE_URL) From cf2940be05954427a89547203968014002b743e3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 17:58:10 -0300 Subject: [PATCH 046/213] =?UTF-8?q?=E2=9C=A8=20feat(session):=20add=20Sess?= =?UTF-8?q?ionManagerFactory,=20SessionManager,=20and=20related=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added `SessionManagerFactory` class in `src/backend/langflow/services/session/factory.py` to create instances of `SessionManager`. - Added `SessionManager` class in `src/backend/langflow/services/session/manager.py` to manage sessions. - Added `load_session`, `generate_key`, `update_session`, and `clear_session` methods to `SessionManager` to handle session-related logic. These changes were made to introduce session management functionality to the application. --- .../langflow/services/session/__init__.py | 0 .../langflow/services/session/factory.py | 14 +++++++ .../langflow/services/session/manager.py | 38 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/backend/langflow/services/session/__init__.py create mode 100644 src/backend/langflow/services/session/factory.py create mode 100644 src/backend/langflow/services/session/manager.py diff --git a/src/backend/langflow/services/session/__init__.py b/src/backend/langflow/services/session/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/services/session/factory.py b/src/backend/langflow/services/session/factory.py new file mode 100644 index 000000000..42b33e70f --- /dev/null +++ b/src/backend/langflow/services/session/factory.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING +from langflow.services.session.manager import SessionManager +from langflow.services.factory import ServiceFactory + +if TYPE_CHECKING: + from langflow.services.cache.manager import BaseCacheManager + + +class SessionManagerFactory(ServiceFactory): + def __init__(self): + super().__init__(SessionManager) + + def create(self, cache_manager: "BaseCacheManager"): + return SessionManager(cache_manager) diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py new file mode 100644 index 000000000..39cefba24 --- /dev/null +++ b/src/backend/langflow/services/session/manager.py @@ -0,0 +1,38 @@ +from langflow.interface.run import build_sorted_vertices +from langflow.services.base import Service +from langflow.services.cache.utils import compute_dict_hash + + +class SessionManager(Service): + name = "session_manager" + + def __init__(self, cache_manager): + self.cache_manager = cache_manager + + def load_session(self, session_id, data_graph): + key = self.generate_key(session_id, data_graph) + + # Check if the data is cached + if key in self.cache_manager: + return self.cache_manager.get(key) + + # If not cached, build the graph and cache it + graph, artifacts = build_sorted_vertices(data_graph) + self.cache_manager.set(key, (graph, artifacts)) + + return graph, artifacts + + def generate_key(self, session_id, data_graph): + # Hash the JSON and combine it with the session_id to create a unique key + json_hash = compute_dict_hash(data_graph) + return f"{session_id}{':' if session_id else ''}{json_hash}" + + def update_session(self, session_id, data_graph, value): + key = self.generate_key(session_id, data_graph) + self.cache_manager.set(key, value) + + def clear_session(self, session_id, data_graph): + key = self.generate_key(session_id, data_graph) + self.cache_manager.delete(key) + + # Additional methods to handle session-related logic From 632eef48b2dbf9a3588bf0bcfc263f828c2528a8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 17:59:00 -0300 Subject: [PATCH 047/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20chang?= =?UTF-8?q?e=20threading.Lock=20to=20threading.RLock=20to=20allow=20reentr?= =?UTF-8?q?ant=20locking=20and=20prevent=20deadlocks=20=F0=9F=94=80=20merg?= =?UTF-8?q?e(manager.py):=20refactor=20=5Fget=20method=20to=20extract=20lo?= =?UTF-8?q?gic=20into=20=5Fget=5Fwithout=5Flock=20method=20for=20code=20re?= =?UTF-8?q?use=20and=20readability=20=F0=9F=94=80=20merge(manager.py):=20r?= =?UTF-8?q?efactor=20set=20method=20to=20use=20=5Fget=5Fwithout=5Flock=20m?= =?UTF-8?q?ethod=20for=20code=20reuse=20and=20readability=20=F0=9F=94=80?= =?UTF-8?q?=20merge(manager.py):=20add=20lock=20acquisition=20in=20delete?= =?UTF-8?q?=20method=20to=20ensure=20thread=20safety=20=F0=9F=94=80=20merg?= =?UTF-8?q?e(utils.py):=20add=20update=5Fcache=20method=20to=20Memoize=20c?= =?UTF-8?q?lass=20for=20updating=20cache=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/cache/manager.py | 37 +++++++++++-------- src/backend/langflow/services/cache/utils.py | 5 +++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index ec9909c75..a3f04518d 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -43,7 +43,7 @@ class InMemoryCache(BaseCacheManager): expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. """ self._cache = OrderedDict() - self._lock = threading.Lock() + self._lock = threading.RLock() self.max_size = max_size self.expiration_time = expiration_time @@ -58,18 +58,23 @@ class InMemoryCache(BaseCacheManager): The value associated with the key, or None if the key is not found or the item has expired. """ with self._lock: - if key in self._cache: - item = self._cache.pop(key) - if ( - self.expiration_time is None - or time.time() - item["time"] < self.expiration_time - ): - # Move the key to the end to make it recently used - self._cache[key] = item - return item["value"] - else: - self.delete(key) - return None + return self._get_without_lock(key) + + def _get_without_lock(self, key): + """ + Retrieve an item from the cache without acquiring the lock. + """ + if item := self._cache.get(key): + if ( + self.expiration_time is None + or time.time() - item["time"] < self.expiration_time + ): + # Move the key to the end to make it recently used + self._cache.move_to_end(key) + return item["value"] + else: + self.delete(key) + return None def set(self, key, value): """ @@ -100,7 +105,7 @@ class InMemoryCache(BaseCacheManager): value: The value to insert or update. """ with self._lock: - existing_value = self.get(key) + existing_value = self._get_without_lock(key) if ( existing_value is not None and isinstance(existing_value, dict) @@ -135,8 +140,8 @@ class InMemoryCache(BaseCacheManager): Args: key: The key of the item to remove. """ - # with self._lock: - self._cache.pop(key, None) + with self._lock: + self._cache.pop(key, None) def clear(self): """ diff --git a/src/backend/langflow/services/cache/utils.py b/src/backend/langflow/services/cache/utils.py index 13e24dd6d..f73b8f023 100644 --- a/src/backend/langflow/services/cache/utils.py +++ b/src/backend/langflow/services/cache/utils.py @@ -212,6 +212,10 @@ class Memoize: cache_manager = self.get_cache_manager() return cache_manager.get(session_id) + def update_cache(self, session_id, value): + cache_manager = self.get_cache_manager() + cache_manager.set(session_id, value) + def __call__(self, func: Callable[..., Any]): @functools.wraps(func) def wrapper(*args, **kwargs): @@ -228,5 +232,6 @@ class Memoize: return result wrapper.clear_cache = self.clear_cache + wrapper.update_cache = self.update_cache wrapper.get_result_by_session_id = self.get_result_by_session_id return wrapper From 26e6c325aafaa07d233c9194f069e354ff3b98f1 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 18:00:27 -0300 Subject: [PATCH 048/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20add?= =?UTF-8?q?=20support=20for=20registering=20factory=20dependencies=20to=20?= =?UTF-8?q?handle=20service=20dependencies=20=E2=9C=A8=20feat(manager.py):?= =?UTF-8?q?=20add=20support=20for=20handling=20service=20dependencies=20wh?= =?UTF-8?q?en=20creating=20services=20=F0=9F=94=A7=20chore(manager.py):=20?= =?UTF-8?q?comment=20out=20validation=20for=20creating=20services=20before?= =?UTF-8?q?=20the=20settings=20service=20=F0=9F=94=A7=20chore(utils.py):?= =?UTF-8?q?=20add=20get=5Fsession=5Fmanager()=20function=20to=20retrieve?= =?UTF-8?q?=20the=20session=20manager=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/manager.py | 68 ++++++++++++++++-------- src/backend/langflow/services/schema.py | 1 + src/backend/langflow/services/utils.py | 4 ++ 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index 1fc87ac7d..d02f0fd1c 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -1,5 +1,5 @@ from langflow.services.schema import ServiceType -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, List if TYPE_CHECKING: from langflow.services.factory import ServiceFactory @@ -13,13 +13,19 @@ class ServiceManager: def __init__(self): self.services = {} self.factories = {} + self.dependencies = {} - def register_factory(self, service_factory: "ServiceFactory"): + def register_factory( + self, service_factory: "ServiceFactory", dependencies: List[ServiceType] = None + ): """ - Registers a new factory. + Registers a new factory with dependencies. """ - if service_factory.service_class.name not in self.factories: - self.factories[service_factory.service_class.name] = service_factory + if dependencies is None: + dependencies = [] + service_name = service_factory.service_class.name + self.factories[service_name] = service_factory + self.dependencies[service_name] = dependencies def get(self, service_name: ServiceType): """ @@ -32,17 +38,25 @@ class ServiceManager: def _create_service(self, service_name: ServiceType): """ - Create a new service given its name. + Create a new service given its name, handling dependencies. """ self._validate_service_creation(service_name) - if service_name == ServiceType.SETTINGS_MANAGER: - self.services[service_name] = self.factories[service_name].create() - else: - settings_service = self.get(ServiceType.SETTINGS_MANAGER) - self.services[service_name] = self.factories[service_name].create( - settings_service - ) + # Create dependencies first + for dependency in self.dependencies.get(service_name, []): + if dependency not in self.services: + self._create_service(dependency) + + # Collect the dependent services + dependent_services = { + dep.value: self.services[dep] + for dep in self.dependencies.get(service_name, []) + } + + # Create the actual service + self.services[service_name] = self.factories[service_name].create( + **dependent_services + ) def _validate_service_creation(self, service_name: ServiceType): """ @@ -53,13 +67,13 @@ class ServiceManager: f"No factory registered for the service class '{service_name.name}'" ) - if ( - ServiceType.SETTINGS_MANAGER not in self.factories - and service_name != ServiceType.SETTINGS_MANAGER - ): - raise ValueError( - f"Cannot create service '{service_name.name}' before the settings service" - ) + # if ( + # ServiceType.SETTINGS_MANAGER not in self.factories + # and service_name != ServiceType.SETTINGS_MANAGER + # ): + # raise ValueError( + # f"Cannot create service '{service_name.name}' before the settings service" + # ) def update(self, service_name: ServiceType): """ @@ -81,11 +95,21 @@ def initialize_services(): from langflow.services.cache import factory as cache_factory from langflow.services.chat import factory as chat_factory from langflow.services.settings import factory as settings_factory + from langflow.services.session import factory as session_manager_factory service_manager.register_factory(settings_factory.SettingsManagerFactory()) - service_manager.register_factory(database_factory.DatabaseManagerFactory()) - service_manager.register_factory(cache_factory.CacheManagerFactory()) + service_manager.register_factory( + database_factory.DatabaseManagerFactory(), + dependencies=[ServiceType.SETTINGS_MANAGER], + ) + service_manager.register_factory( + cache_factory.CacheManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + ) service_manager.register_factory(chat_factory.ChatManagerFactory()) + service_manager.register_factory( + session_manager_factory.SessionManagerFactory(), + dependencies=[ServiceType.CACHE_MANAGER], + ) # Test cache connection service_manager.get(ServiceType.CACHE_MANAGER) diff --git a/src/backend/langflow/services/schema.py b/src/backend/langflow/services/schema.py index 695763afc..852175f46 100644 --- a/src/backend/langflow/services/schema.py +++ b/src/backend/langflow/services/schema.py @@ -11,3 +11,4 @@ class ServiceType(str, Enum): SETTINGS_MANAGER = "settings_manager" DATABASE_MANAGER = "database_manager" CHAT_MANAGER = "chat_manager" + SESSION_MANAGER = "session_manager" diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index 1c5796fb4..befa31f07 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -20,3 +20,7 @@ def get_session(): def get_cache_manager(): return service_manager.get(ServiceType.CACHE_MANAGER) + + +def get_session_manager(): + return service_manager.get(ServiceType.SESSION_MANAGER) From 79b48efddfed4caf4741cae26e09ef89b694b86e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 17 Aug 2023 18:09:33 -0300 Subject: [PATCH 049/213] =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.yml?= =?UTF-8?q?):=20update=20volume=20path=20to=20correctly=20mount=20parent?= =?UTF-8?q?=20directory=20for=20the=20app=20=F0=9F=94=A7=20chore(docker-co?= =?UTF-8?q?mpose.yml):=20update=20Redis=20image=20version=20to=207.2.0=20f?= =?UTF-8?q?or=20the=20queue=20service=20=F0=9F=94=A7=20chore(docker-compos?= =?UTF-8?q?e.yml):=20update=20command=20for=20the=20flower=20service=20to?= =?UTF-8?q?=20use=20environment=20variables=20for=20Redis=20connection=20d?= =?UTF-8?q?etails=20=F0=9F=94=A7=20chore(flower.env):=20add=20LANGFLOW=5FR?= =?UTF-8?q?EDIS=5FPASSWORD=20environment=20variable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.yml | 6 +++--- deploy/flower.env | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 293115182..0349fba41 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -14,7 +14,7 @@ services: ports: - "7860:7860" volumes: - - ./:/app + - ../:/app - ./startup-backend.sh:/startup-backend.sh # Ensure the paths match command: /startup-backend.sh # Fixed the path @@ -37,7 +37,7 @@ services: - ./pgadmin:/var/lib/pgadmin queue: - image: redis:6.2.5 # Use a specific version + image: redis:7.2.0 ports: - "6379:6379" @@ -63,7 +63,7 @@ services: dockerfile: base.Dockerfile env_file: - ./flower.env - command: celery -A langflow.worker.celery_app --broker=redis://queue:6379/0 flower --port=5555 + command: /bin/sh -c "celery -A langflow.worker.celery_app --broker=redis://$${LANGFLOW_REDIS_PASSWORD:+default:$${LANGFLOW_REDIS_PASSWORD}@}$${LANGFLOW_REDIS_HOST}:$${LANGFLOW_REDIS_PORT}/$${LANGFLOW_REDIS_DB} flower --port=5555" ports: - "5555:5555" diff --git a/deploy/flower.env b/deploy/flower.env index df742505f..93e9f6e5d 100644 --- a/deploy/flower.env +++ b/deploy/flower.env @@ -3,3 +3,4 @@ LANGFLOW_REDIS_HOST=queue LANGFLOW_REDIS_PORT=6379 LANGFLOW_REDIS_DB=0 LANGFLOW_REDIS_EXPIRE=3600 +LANGFLOW_REDIS_PASSWORD= From a7ff7da7789d8f4fa3b7752cfe1d6e1150872741 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:51:30 -0300 Subject: [PATCH 050/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.Dockerfile):=20?= =?UTF-8?q?remove=20unnecessary=20copy=20of=20./src/backend=20directory=20?= =?UTF-8?q?to=20improve=20build=20performance=20=E2=9C=A8=20feat(base.Dock?= =?UTF-8?q?erfile):=20add=20healthcheck=20for=20queue=20and=20celeryworker?= =?UTF-8?q?=20services=20to=20ensure=20their=20availability=20=E2=9C=A8=20?= =?UTF-8?q?feat(docker-compose.yml):=20add=20healthcheck=20for=20queue,=20?= =?UTF-8?q?celeryworker,=20and=20flower=20services=20to=20ensure=20their?= =?UTF-8?q?=20availability=20=F0=9F=90=9B=20fix(docker-compose.yml):=20fix?= =?UTF-8?q?=20the=20path=20in=20the=20command=20for=20backend=20service=20?= =?UTF-8?q?startup=20=F0=9F=90=9B=20fix(flower.env):=20add=20missing=20dou?= =?UTF-8?q?ble=20quote=20at=20the=20end=20of=20C=5FFORCE=5FROOT=20value=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(startup-backend.sh):=20remove=20unnecessary?= =?UTF-8?q?=20--reload=20flag=20from=20uvicorn=20command=20and=20add=20--w?= =?UTF-8?q?orkers=20-1=20flag=20to=20utilize=20all=20available=20CPU=20cor?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/base.Dockerfile | 2 +- deploy/docker-compose.yml | 20 ++++++++++++++++---- deploy/flower.env | 3 +++ deploy/startup-backend.sh | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/deploy/base.Dockerfile b/deploy/base.Dockerfile index 1be280163..83e875fa6 100644 --- a/deploy/base.Dockerfile +++ b/deploy/base.Dockerfile @@ -63,7 +63,6 @@ RUN --mount=type=cache,target=/root/.cache \ # copy project requirement files here to ensure they will be cached. WORKDIR $PYSETUP_PATH COPY ./poetry.lock ./pyproject.toml ./ -COPY ./src/backend ./src/backend # Copy README.md to the build context COPY ./README.md ./ # install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally @@ -82,6 +81,7 @@ WORKDIR $PYSETUP_PATH COPY --from=builder-base $POETRY_HOME $POETRY_HOME COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH +COPY ./src/backend ./src/backend # quicker install as runtime deps are already installed RUN --mount=type=cache,target=/root/.cache \ poetry install --with=dev --extras deploy diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 0349fba41..fcdbf529b 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -17,6 +17,8 @@ services: - ../:/app - ./startup-backend.sh:/startup-backend.sh # Ensure the paths match command: /startup-backend.sh # Fixed the path + healthcheck: + test: "exit 0" db: image: postgres:15.4 @@ -37,27 +39,35 @@ services: - ./pgadmin:/var/lib/pgadmin queue: - image: redis:7.2.0 + image: redis:6.2.5 ports: - "6379:6379" + healthcheck: + test: "exit 0" celeryworker: # user: your-non-root-user depends_on: - - queue + queue: + condition: service_healthy env_file: - ./celeryworker.env build: context: ../ dockerfile: base.Dockerfile command: celery -A langflow.worker.celery_app worker --loglevel=INFO + healthcheck: + test: "exit 0" flower: # user: your-non-root-user networks: - default depends_on: - - queue + queue: + condition: service_healthy + celeryworker: + condition: service_healthy build: context: ../ dockerfile: base.Dockerfile @@ -75,7 +85,9 @@ services: args: - BACKEND_URL=http://backend:7860 depends_on: - - backend + backend: + condition: service_healthy + env_file: - ./frontend.env ports: diff --git a/deploy/flower.env b/deploy/flower.env index 93e9f6e5d..845de63f5 100644 --- a/deploy/flower.env +++ b/deploy/flower.env @@ -4,3 +4,6 @@ LANGFLOW_REDIS_PORT=6379 LANGFLOW_REDIS_DB=0 LANGFLOW_REDIS_EXPIRE=3600 LANGFLOW_REDIS_PASSWORD= +BROKER_URL=redis://queue:6379/0 +RESULT_BACKEND=redis://queue:6379/0 +C_FORCE_ROOT="true # ! Only for development" \ No newline at end of file diff --git a/deploy/startup-backend.sh b/deploy/startup-backend.sh index cf63444b5..c8a634f65 100755 --- a/deploy/startup-backend.sh +++ b/deploy/startup-backend.sh @@ -3,4 +3,4 @@ export LANGFLOW_DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}" # Your command to start the backend -exec uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level ${LOG_LEVEL:-info} +exec python -m uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --log-level ${LOG_LEVEL:-info} --workers -1 From 853648ae332b93557292ebc853295d40fe80b9b3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:51:56 -0300 Subject: [PATCH 051/213] =?UTF-8?q?=F0=9F=93=9D=20chore(locustfile.py):=20?= =?UTF-8?q?add=20locustfile.py=20for=20load=20testing=20the=20API=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(test=5Fprocess.py):=20refactor=20test=5Fload?= =?UTF-8?q?=5Flangchain=5Fobject=5Fwith=5Fcached=5Fsession=20to=20use=20se?= =?UTF-8?q?ssion=5Fmanager=20=F0=9F=90=9B=20fix(test=5Fprocess.py):=20refa?= =?UTF-8?q?ctor=20test=5Fload=5Flangchain=5Fobject=5Fwith=5Fno=5Fcached=5F?= =?UTF-8?q?session=20to=20use=20session=5Fmanager=20=F0=9F=90=9B=20fix(tes?= =?UTF-8?q?t=5Fprocess.py):=20refactor=20test=5Fload=5Flangchain=5Fobject?= =?UTF-8?q?=5Fwithout=5Fsession=5Fid=20to=20use=20session=5Fmanager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/package-lock.json | 774 --------------------------------- tests/locust/locustfile.py | 47 ++ tests/test_process.py | 53 ++- 3 files changed, 73 insertions(+), 801 deletions(-) create mode 100644 tests/locust/locustfile.py diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index d28f74b68..039c70e81 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -707,51 +707,6 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, - "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/darwin-arm64": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", @@ -767,276 +722,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@floating-ui/core": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", @@ -2904,150 +2589,6 @@ "node": ">=10" } }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.74.tgz", - "integrity": "sha512-KKEGE1wXneYXe15fWDRM8/oekd/Q4yAuccA0vWY/7i6nOSPqWYcSDR0nRtR030ltDxWt0rk/eCTmNkrOWrKs3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.74.tgz", - "integrity": "sha512-HehH5DR6r/5fIVu7tu8ZqgrHkhSCQNewf1ztFQJgcmaQWn+H4AJERBjwkjosqh4TvUJucZv8vyRTvrFeBXaCSA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.74.tgz", - "integrity": "sha512-+xkbCRz/wczgdknoV4NwYxbRI2dD7x/qkIFcVM2buzLCq8oWLweuV8+aL4pRqu0qDh7ZSb1jcaVTUIsySCJznA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.74.tgz", - "integrity": "sha512-maKFZSCD3tQznzPV7T3V+TtiWZFEFM8YrnSS5fQNNb+K9J65sL+170uTb3M7H4cFkG+9Sm5k5yCrCIutlvV48g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.74.tgz", - "integrity": "sha512-LEXpcShF6DLTWJSiBhMSYZkLQ27UvaQ24fCFhoIV/R3dhYaUpHmIyLPPBNC82T03lB3ONUFVwrRw6fxDJ/f00A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.74.tgz", - "integrity": "sha512-sxsFctbFMZEFmDE7CmYljG0dMumH8XBTwwtGr8s6z0fYAzXBGNq2AFPcmEh2np9rPWkt7pE1m0ByESD+dMkbxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.74.tgz", - "integrity": "sha512-F7hY9/BjFCozA4YPFYFH5FGCyWwa44vIXHqG66F5cDwXDGFn8ZtBsYIsiPfUYcx0AeAo1ojnVWKPxokZhYNYqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.74.tgz", - "integrity": "sha512-qBAsiD1AlIdqED6wy3UNRHyAys9pWMUidX0LJ6mj24r/vfrzzTBAUrLJe5m7bzE+F1Rgi001avYJeEW1DLEJ+Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.74", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.74.tgz", - "integrity": "sha512-S3YAvvLprTnPRwQuy9Dkwubb5SRLpVK3JJsqYDbGfgj8PGQyKHZcVJ5X3nfFsoWLy3j9B/3Os2nawprRSzeC5A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -10530,51 +10071,6 @@ "vite": "^2.6.0 || 3 || 4" } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.19.tgz", - "integrity": "sha512-1uOoDurJYh5MNqPqpj3l/TQCI1V25BXgChEldCB7D6iryBYqYKrbZIhYO5AI9fulf66sM8UJpc3UcCly2Tv28w==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.19.tgz", - "integrity": "sha512-4+jkUFQxZkQfQOOxfGVZB38YUWHMJX2ihZwF+2nh8m7bHdWXpixiurgGRN3c/KMSwlltbYI0/i929jwBRMFzbA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.19.tgz", - "integrity": "sha512-ae5sHYiP/Ogj2YNrLZbWkBmyHIDOhPgpkGvFnke7XFGQldBDWvc/AyYwSLpNuKw9UNkgnLlB/jPpnBmlF3G9Bg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { "version": "0.18.19", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.19.tgz", @@ -10590,276 +10086,6 @@ "node": ">=12" } }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.19.tgz", - "integrity": "sha512-m6JdvXJQt0thNLIcWOeG079h2ivhYH4B5sVCgqb/B29zTcFd7EE8/J1nIUHhdtwGeItdUeqKaqqb4towwxvglQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.19.tgz", - "integrity": "sha512-G0p4EFMPZhGn/xVNspUyMQbORH3nlKTV0bFNHPIwLraBuAkTeMyxNviTe0ZXUbIXQrR1lrwniFjNFU4s+x7veQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.19.tgz", - "integrity": "sha512-hBxgRlG42+W+j/1/cvlnSa+3+OBKeDCyO7OG2ICya1YJaSCYfSpuG30KfOnQHI7Ytgu4bRqCgrYXxQEzy0zM5Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.19.tgz", - "integrity": "sha512-qtWyoQskfJlb9MD45mvzCEKeO4uCnDZ7lPFeNqbfaaJHqBiH9qA5Vu2EuckqYZuFMJWy1l4dxTf9NOulCVfUjg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.19.tgz", - "integrity": "sha512-X8g33tczY0GsJq3lhyBrjnFtaKjWVpp1gMq5IlF9BQJ3TUfSK74nQnz9mRIEejmcV+OIYn6bkOJeUaU1Knrljg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.19.tgz", - "integrity": "sha512-SAkRWJgb+KN+gOhmbiE6/wu23D6HRcGQi15cB13IVtBZZgXxygTV5GJlUAKLQ5Gcx0gtlmt+XIxEmSqA6sZTOw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.19.tgz", - "integrity": "sha512-YLAslaO8NsB9UOxBchos82AOMRDbIAWChwDKfjlGrHSzS3v1kxce7dGlSTsrb0PJwo1KYccypN3VNjQVLtz7LA==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.19.tgz", - "integrity": "sha512-vSYFtlYds/oTI8aflEP65xo3MXChMwBOG1eWPGGKs/ev9zkTeXVvciU+nifq8J1JYMz+eQ4J9JDN0O2RKF8+1Q==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.19.tgz", - "integrity": "sha512-tgG41lRVwlzqO9tv9l7aXYVw35BxKXLtPam1qALScwSqPivI8hjkZLNH0deaaSCYCFT9cBIdB+hUjWFlFFLL9A==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.19.tgz", - "integrity": "sha512-EgBZFLoN1S5RuB4cCJI31pBPsjE1nZ+3+fHRjguq9Ibrzo29bOLSBcH1KZJvRNh5qtd+fcYIGiIUia8Jw5r1lQ==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.19.tgz", - "integrity": "sha512-q1V1rtHRojAzjSigZEqrcLkpfh5K09ShCoIsdTakozVBnM5rgV58PLFticqDp5UJ9uE0HScov9QNbbl8HBo6QQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.19.tgz", - "integrity": "sha512-D0IiYjpZRXxGZLQfsydeAD7ZWqdGyFLBj5f2UshJpy09WPs3qizDCsEr8zyzcym6Woj/UI9ZzMIXwvoXVtyt0A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.19.tgz", - "integrity": "sha512-3tt3SOS8L3D54R8oER41UdDshlBIAjYhdWRPiZCTZ1E41+shIZBpTjaW5UaN/jD1ENE/Ok5lkeqhoNMbxstyxw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.19.tgz", - "integrity": "sha512-MxbhcuAYQPlfln1EMc4T26OUoeg/YQc6wNoEV8xvktDKZhLtBxjkoeESSo9BbPaGKhAPzusXYj5n8n5A8iZSrA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.19.tgz", - "integrity": "sha512-m0/UOq1wj25JpWqOJxoWBRM9VWc3c32xiNzd+ERlYstUZ6uwx5SZsQUtkiFHaYmcaoj+f6+Tfcl7atuAz3idwQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.19.tgz", - "integrity": "sha512-L4vb6pcoB1cEcXUHU6EPnUhUc4+/tcz4OqlXTWPcSQWxegfmcOprhmIleKKwmMNQVc4wrx/+jB7tGkjjDmiupg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.19.tgz", - "integrity": "sha512-rQng7LXSKdrDlNDb7/v0fujob6X0GAazoK/IPd9C3oShr642ri8uIBkgM37/l8B3Rd5sBQcqUXoDdEy75XC/jg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.18.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.19.tgz", - "integrity": "sha512-z69jhyG20Gq4QL5JKPLqUT+eREuqnDAFItLbza4JCmpvUnIlY73YNjd5djlO7kBiiZnvTnJuAbOjIoZIOa1GjA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/vite/node_modules/esbuild": { "version": "0.18.19", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.19.tgz", diff --git a/tests/locust/locustfile.py b/tests/locust/locustfile.py new file mode 100644 index 000000000..80b655f5b --- /dev/null +++ b/tests/locust/locustfile.py @@ -0,0 +1,47 @@ +from locust import HttpUser, task, between +import random + + +class NameTest(HttpUser): + host = "http://localhost:/api/v1" + wait_time = between(1, 5) + + # Read names from the file + with open("names.txt", "r") as file: + names = [line.strip() for line in file.readlines()] + + @task + def send_name_and_check(self): + # Select a random name or in order from the list + name = random.choice(self.names) + flow_id = "0bc439e4-539c-4b18-9813-92729326b171" # Replace with the appropriate flow ID + random_session_id_with_name = f"{name}-{random.randint(0, 1000000)}" + session_id = None + # First input + payload1 = { + "inputs": {"text": f"Hello, My name is {name}"}, + "session_id": random_session_id_with_name, + } + with self.client.post( + f"/process/{flow_id}", json=payload1, catch_response=True + ) as response: + if response.status_code != 200: + response.failure(f"Error: {response.json()}") + else: + response.success() + + session_id = response.json().get("session_id") + print(f"Session ID: {session_id}") + + if not session_id: + raise ValueError("Session ID not found") + + # Second input + payload2 = {"inputs": {"text": "What is my name?"}, "session_id": session_id} + with self.client.post( + f"/process/{flow_id}", json=payload2, catch_response=True + ) as response2: + if name not in response2.text: + response2.failure(f"Error {name} not in response: {response2.json()}") + else: + response2.success() diff --git a/tests/test_process.py b/tests/test_process.py index 29c125abf..6d396f974 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -1,5 +1,5 @@ -from langflow.interface.run import build_sorted_vertices_with_caching -from langflow.processing.process import load_langchain_object, process_tweaks +from langflow.processing.process import process_tweaks +from langflow.services.utils import get_session_manager def test_no_tweaks(): @@ -198,51 +198,50 @@ def test_tweak_not_in_template(): def test_load_langchain_object_with_cached_session(client, basic_graph_data): - # Build the langchain_object once and get the session_id - langchain_object1, artifacts1, session_id1 = load_langchain_object( - basic_graph_data, None + # Provide a non-existent session_id + session_manager = get_session_manager() + session_id1 = "non-existent-session-id" + langchain_object1, artifacts1 = session_manager.load_session( + session_id1, basic_graph_data ) - # Use the same session_id to get the langchain_object again - langchain_object2, artifacts2, session_id2 = load_langchain_object( - basic_graph_data, session_id1 + # Use the new session_id to get the langchain_object again + langchain_object2, artifacts2 = session_manager.load_session( + session_id1, basic_graph_data ) - assert session_id1 == session_id2 assert id(langchain_object1) == id(langchain_object2) assert artifacts1 == artifacts2 def test_load_langchain_object_with_no_cached_session(client, basic_graph_data): # Provide a non-existent session_id - langchain_object1, artifacts1, session_id1 = load_langchain_object( - basic_graph_data, "non_existent_session" + session_manager = get_session_manager() + session_id1 = "non-existent-session-id" + langchain_object1, artifacts1 = session_manager.load_session( + session_id1, basic_graph_data ) # Clear the cache - build_sorted_vertices_with_caching.clear_cache(session_id1) + session_manager.clear_session(session_id1, basic_graph_data) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2, session_id2 = load_langchain_object( - basic_graph_data, session_id1 + langchain_object2, artifacts2 = session_manager.load_session( + session_id1, basic_graph_data ) - assert session_id1 == session_id2 assert id(langchain_object1) != id( langchain_object2 ) # Since the cache was cleared, objects should be different def test_load_langchain_object_without_session_id(client, basic_graph_data): - # Build the langchain_object without providing a session_id - langchain_object1, artifacts1, session_id1 = load_langchain_object( - basic_graph_data, None + # Provide a non-existent session_id + session_manager = get_session_manager() + session_id1 = None + langchain_object1, artifacts1 = session_manager.load_session( + session_id1, basic_graph_data ) - # Build the langchain_object again without providing a session_id - langchain_object2, artifacts2, session_id2 = load_langchain_object( - basic_graph_data, None + # Use the new session_id to get the langchain_object again + langchain_object2, artifacts2 = session_manager.load_session( + session_id1, basic_graph_data ) - assert session_id1 == session_id2 - - assert id(langchain_object1) == id( - langchain_object2 - ) # Since no session_id was provided, the hash will be based on the graph_data - assert artifacts1 == artifacts2 + assert id(langchain_object1) == id(langchain_object2) From f4a0c9f4367dae4697abdd4a699b9c82f2656153 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:52:26 -0300 Subject: [PATCH 052/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20remov?= =?UTF-8?q?e=20commented=20code=20block=20to=20improve=20code=20readabilit?= =?UTF-8?q?y=20and=20maintainability=20=E2=9C=A8=20feat(manager.py):=20add?= =?UTF-8?q?=20initialization=20for=20session=20manager=20and=20its=20depen?= =?UTF-8?q?dencies=20(cache=20manager=20and=20settings=20manager)=20to=20e?= =?UTF-8?q?nable=20session=20management=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/chat/manager.py | 1 + src/backend/langflow/services/manager.py | 27 +++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index 394455b22..3eb43b810 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -137,6 +137,7 @@ class ChatManager(Service): chat_inputs=chat_inputs, websocket=self.active_connections[client_id], ) + self.set_cache(client_id, langchain_object) except Exception as e: # Log stack trace logger.exception(e) diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index d02f0fd1c..f40ae1f25 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -67,14 +67,6 @@ class ServiceManager: f"No factory registered for the service class '{service_name.name}'" ) - # if ( - # ServiceType.SETTINGS_MANAGER not in self.factories - # and service_name != ServiceType.SETTINGS_MANAGER - # ): - # raise ValueError( - # f"Cannot create service '{service_name.name}' before the settings service" - # ) - def update(self, service_name: ServiceType): """ Update a service by its name. @@ -124,3 +116,22 @@ def initialize_settings_manager(): from langflow.services.settings import factory as settings_factory service_manager.register_factory(settings_factory.SettingsManagerFactory()) + + +def initialize_session_manager(): + """ + Initialize the session manager. + """ + from langflow.services.session import factory as session_manager_factory + from langflow.services.cache import factory as cache_factory + + initialize_settings_manager() + + service_manager.register_factory( + cache_factory.CacheManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + ) + + service_manager.register_factory( + session_manager_factory.SessionManagerFactory(), + dependencies=[ServiceType.CACHE_MANAGER], + ) From c195456c49a3b7f48af4c8f8cb9315ae516f1f79 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:56:58 -0300 Subject: [PATCH 053/213] =?UTF-8?q?=F0=9F=94=A7=20fix(base.py):=20update?= =?UTF-8?q?=20debug=20log=20message=20to=20provide=20more=20meaningful=20i?= =?UTF-8?q?nformation=20about=20the=20number=20of=20vertices=20in=20the=20?= =?UTF-8?q?graph=20=F0=9F=94=A7=20fix(run.py):=20comment=20out=20the=20cac?= =?UTF-8?q?hing=20functionality=20for=20building=20sorted=20vertices=20to?= =?UTF-8?q?=20prevent=20caching=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/graph/base.py | 2 +- src/backend/langflow/interface/run.py | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/graph/graph/base.py b/src/backend/langflow/graph/graph/base.py index f0d3986cf..d5225c489 100644 --- a/src/backend/langflow/graph/graph/base.py +++ b/src/backend/langflow/graph/graph/base.py @@ -147,7 +147,7 @@ class Graph: def generator_build(self) -> Generator: """Builds each vertex in the graph and yields it.""" sorted_vertices = self.topological_sort() - logger.debug("Sorted vertices: %s", sorted_vertices) + logger.debug("There are %s vertices in the graph", len(sorted_vertices)) yield from sorted_vertices def get_node_neighbors(self, node: Vertex) -> Dict[Vertex, int]: diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 7b7a16b69..04e5df046 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -16,8 +16,8 @@ def build_langchain_object_with_caching(data_graph): return graph.build() -@Memoize(get_cache_manager=get_cache_manager) -def build_sorted_vertices_with_caching(data_graph) -> Tuple[Any, Dict]: +# @Memoize(get_cache_manager=get_cache_manager) +def build_sorted_vertices(data_graph) -> Tuple[Any, Dict]: """ Build langchain object from data_graph. """ @@ -32,6 +32,23 @@ def build_sorted_vertices_with_caching(data_graph) -> Tuple[Any, Dict]: artifacts.update(vertex.artifacts) return graph.build(), artifacts + # def build_sorted_vertices_with_caching(data_graph): + # # Build the result if not in cache + # logger.debug("Building langchain object") + # graph = Graph.from_payload(data_graph) + # sorted_vertices = graph.topological_sort() + # artifacts = {} + # for vertex in sorted_vertices: + # vertex.build() + # if vertex.artifacts: + # artifacts.update(vertex.artifacts) + # result = (graph.build(), artifacts) + + # Save to cache + # cache_manager.set(session_id, result) + + # return result + def build_langchain_object(data_graph): """ From 682e611947e5de0c736943fe5a372a433e01ff7c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:57:12 -0300 Subject: [PATCH 054/213] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20rem?= =?UTF-8?q?ove=20unused=20import=20and=20function=20call=20to=20process=5F?= =?UTF-8?q?graph=5Fcached=20=F0=9F=94=A7=20fix(worker.py):=20refactor=20pr?= =?UTF-8?q?ocess=5Fgraph=5Fcached=20into=20process=5Fgraph=5Fcached=5Ftask?= =?UTF-8?q?=20and=20update=20function=20signature=20and=20implementation?= =?UTF-8?q?=20to=20use=20SessionManager=20for=20loading=20and=20updating?= =?UTF-8?q?=20the=20graph?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 7 ++- src/backend/langflow/worker.py | 79 ++++++++---------------- 2 files changed, 31 insertions(+), 55 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index a6b85dde2..999f751d5 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -3,7 +3,7 @@ from typing import Annotated, Optional, Union from langflow.services.cache.utils import save_uploaded_file from langflow.services.database.models.flow import Flow -from langflow.processing.process import process_graph_cached, process_tweaks +from langflow.processing.process import process_tweaks from langflow.services.utils import get_settings_manager from langflow.utils.logger import logger from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body @@ -26,6 +26,7 @@ from langflow.interface.types import ( ) from langflow.services.utils import get_session +from langflow.worker import process_graph_cached_task from sqlmodel import Session # build router @@ -95,9 +96,9 @@ async def process_flow( graph_data = process_tweaks(graph_data, tweaks) except Exception as exc: logger.error(f"Error processing tweaks: {exc}") - response, session_id = process_graph_cached( + response, session_id = process_graph_cached_task.delay( graph_data, inputs, clear_cache, session_id - ) + ).get() return ProcessResponse(result=response, session_id=session_id) except Exception as e: # Log stack trace diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index f8831df76..4790090f0 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -1,7 +1,14 @@ from langflow.core.celery_app import celery_app -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Tuple from typing import TYPE_CHECKING from celery.exceptions import SoftTimeLimitExceeded +from langflow.processing.process import ( + clear_caches_if_needed, + generate_result, + process_inputs, +) +from langflow.services.manager import initialize_session_manager +from langflow.services.utils import get_session_manager if TYPE_CHECKING: from langflow.graph.vertex.base import Vertex @@ -28,55 +35,23 @@ def build_vertex(self, vertex: "Vertex") -> "Vertex": @celery_app.task(acks_late=True) -def process_graph_cached( - data_graph: Dict[str, Any], inputs: Optional[dict] = None, clear_cache=False -): - """ - Process graph by extracting input variables and replacing ZeroShotPrompt - with PromptTemplate,then run the graph and return the result and thought. - """ - from langflow.interface.run import build_sorted_vertices_with_caching - from langflow.processing.process import get_result_and_thought - from langchain.chains.base import Chain - from langchain.vectorstores.base import VectorStore - from langflow.utils.logger import logger +def process_graph_cached_task( + data_graph: Dict[str, Any], + inputs: Optional[dict] = None, + clear_cache=False, + session_id=None, +) -> Tuple[Any, str]: + initialize_session_manager() + clear_caches_if_needed(clear_cache) + session_manager = get_session_manager() + # Load the graph using SessionManager + langchain_object, artifacts = session_manager.load_session(session_id, data_graph) + processed_inputs = process_inputs(inputs, artifacts) + result = generate_result(langchain_object, processed_inputs) + # langchain_object is now updated with the new memory + # we need to update the cache with the updated langchain_object + session_manager.update_session( + session_id, data_graph, (langchain_object, artifacts) + ) - # Load langchain object - if clear_cache: - build_sorted_vertices_with_caching.clear_cache() - logger.debug("Cleared cache") - langchain_object, artifacts = build_sorted_vertices_with_caching(data_graph) - logger.debug("Loaded LangChain object") - if inputs is None: - inputs = {} - - # Add artifacts to inputs - # artifacts can be documents loaded when building - # the flow - for ( - key, - value, - ) in artifacts.items(): - if key not in inputs or not inputs[key]: - inputs[key] = value - - if langchain_object is None: - # Raise user facing error - raise ValueError( - "There was an error loading the langchain_object. Please, check all the nodes and try again." - ) - - # Generate result and thought - if isinstance(langchain_object, Chain): - if inputs is None: - raise ValueError("Inputs must be provided for a Chain") - logger.debug("Generating result and thought") - result = get_result_and_thought(langchain_object, inputs) - logger.debug("Generated result and thought") - elif isinstance(langchain_object, VectorStore): - result = langchain_object.search(**inputs) - else: - raise ValueError( - f"Unknown langchain_object type: {type(langchain_object).__name__}" - ) - return result + return result, session_id From 2183f167cd83cb73a6d829a50257c5d121d11bc5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:57:24 -0300 Subject: [PATCH 055/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20add?= =?UTF-8?q?=20cache=5Fmanager.set()=20to=20store=20langchain=5Fobject=20in?= =?UTF-8?q?=20cache=20after=20successful=20initialization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(celeryconfig.py): fix condition to check if BROKER_URL and RESULT_BACKEND are present in os.environ before using them, fallback to langflow_redis_host and langflow_redis_port if not present --- src/backend/langflow/chat/manager.py | 1 + src/backend/langflow/core/celeryconfig.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py index b4ccd931e..17968e32a 100644 --- a/src/backend/langflow/chat/manager.py +++ b/src/backend/langflow/chat/manager.py @@ -135,6 +135,7 @@ class ChatManager: chat_inputs=chat_inputs, websocket=self.active_connections[client_id], ) + self.cache_manager.set(client_id, langchain_object) except Exception as e: # Log stack trace logger.exception(e) diff --git a/src/backend/langflow/core/celeryconfig.py b/src/backend/langflow/core/celeryconfig.py index d7827b713..6747135ae 100644 --- a/src/backend/langflow/core/celeryconfig.py +++ b/src/backend/langflow/core/celeryconfig.py @@ -3,11 +3,11 @@ import os langflow_redis_host = os.environ.get("LANGFLOW_REDIS_HOST") langflow_redis_port = os.environ.get("LANGFLOW_REDIS_PORT") -if langflow_redis_host and langflow_redis_port: - broker_url = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" - result_backend = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" -else: +if "BROKER_URL" in os.environ and "RESULT_BACKEND" in os.environ: broker_url = os.environ.get("BROKER_URL", "redis://localhost:6379/0") result_backend = os.environ.get("RESULT_BACKEND", "redis://localhost:6379/0") +elif langflow_redis_host and langflow_redis_port: + broker_url = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" + result_backend = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" # tasks should be json or pickle accept_content = ["json", "pickle"] From 7f217bd518fa7d2bc53299183ba49b0c0ae4e48a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:57:38 -0300 Subject: [PATCH 056/213] =?UTF-8?q?=F0=9F=94=A7=20refactor(process.py):=20?= =?UTF-8?q?remove=20unused=20imports=20and=20variables,=20refactor=20funct?= =?UTF-8?q?ion=20names=20for=20clarity=20=E2=9C=A8=20feat(process.py):=20i?= =?UTF-8?q?ntroduce=20SessionManager=20to=20handle=20loading=20and=20updat?= =?UTF-8?q?ing=20langchain=5Fobject=20sessions=20=F0=9F=90=9B=20fix(proces?= =?UTF-8?q?s.py):=20update=20cache=20with=20the=20updated=20langchain=5Fob?= =?UTF-8?q?ject=20after=20processing=20graph?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 46 +++++++++++++++++----- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 71904f79f..99d47f2fc 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -2,10 +2,11 @@ from pathlib import Path from langchain.schema import AgentAction import json from langflow.interface.run import ( - build_sorted_vertices_with_caching, + build_sorted_vertices, get_memory_key, update_memory_keys, ) +from langflow.services.utils import get_session_manager from langflow.utils.logger import logger from langflow.graph import Graph from langchain.chains.base import Chain @@ -92,18 +93,18 @@ def get_build_result(data_graph, session_id): # otherwise, build the graph and return the result if session_id: logger.debug(f"Loading LangChain object from session {session_id}") - result = build_sorted_vertices_with_caching.get_result_by_session_id(session_id) + result = build_sorted_vertices(data_graph=data_graph, session_id=session_id) if result is not None: logger.debug("Loaded LangChain object") return result logger.debug("Building langchain object") - return build_sorted_vertices_with_caching(data_graph) + return build_sorted_vertices(data_graph, session_id) def clear_caches_if_needed(clear_cache: bool): if clear_cache: - build_sorted_vertices_with_caching.clear_cache() + build_sorted_vertices.clear_cache() logger.debug("Cleared cache") @@ -111,7 +112,6 @@ def load_langchain_object( data_graph: Dict[str, Any], session_id: str ) -> Tuple[Union[Chain, VectorStore], Dict[str, Any], str]: langchain_object, artifacts = get_build_result(data_graph, session_id) - session_id = build_sorted_vertices_with_caching.session_id logger.debug("Loaded LangChain object") if langchain_object is None: @@ -139,6 +139,7 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): raise ValueError("Inputs must be provided for a Chain") logger.debug("Generating result and thought") result = get_result_and_thought(langchain_object, inputs) + logger.debug("Generated result and thought") elif isinstance(langchain_object, VectorStore): result = langchain_object.search(**inputs) @@ -150,6 +151,28 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): return result +# def process_graph_cached( +# data_graph: Dict[str, Any], +# inputs: Optional[dict] = None, +# clear_cache=False, +# session_id=None, +# ) -> Tuple[Any, str]: +# clear_caches_if_needed(clear_cache) +# # If session_id is provided, load the langchain_object from the session +# # else build the graph and return the result and the new session_id +# langchain_object, artifacts, session_id = load_langchain_object( +# data_graph, session_id +# ) +# processed_inputs = process_inputs(inputs, artifacts) +# result = generate_result(langchain_object, processed_inputs) +# if result: +# # we need to update the cache with the updated langchain_object +# build_sorted_vertices_with_caching.update_cache( +# session_id, (langchain_object, artifacts) +# ) +# return result, session_id + + def process_graph_cached( data_graph: Dict[str, Any], inputs: Optional[dict] = None, @@ -157,13 +180,16 @@ def process_graph_cached( session_id=None, ) -> Tuple[Any, str]: clear_caches_if_needed(clear_cache) - # If session_id is provided, load the langchain_object from the session - # else build the graph and return the result and the new session_id - langchain_object, artifacts, session_id = load_langchain_object( - data_graph, session_id - ) + session_manager = get_session_manager() + # Load the graph using SessionManager + langchain_object, artifacts = session_manager.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) result = generate_result(langchain_object, processed_inputs) + # langchain_object is now updated with the new memory + # we need to update the cache with the updated langchain_object + session_manager.update_session( + session_id, data_graph, (langchain_object, artifacts) + ) return result, session_id From 8bdde3bc05426c7407792a06c073ce1a269593b9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 08:57:53 -0300 Subject: [PATCH 057/213] =?UTF-8?q?=F0=9F=94=A8=20refactor(custom=5Fcompon?= =?UTF-8?q?ent.py):=20remove=20unused=20import=20and=20update=20function?= =?UTF-8?q?=20name=20for=20better=20code=20organization=20and=20clarity=20?= =?UTF-8?q?=F0=9F=93=9D=20docs(custom=5Fcomponent.py):=20update=20docstrin?= =?UTF-8?q?g=20for=20better=20readability=20and=20understanding=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(custom=5Fcomponent.py):=20add=20new=20file?= =?UTF-8?q?=20names.txt=20for=20locust=20tests=20with=20a=20list=20of=20na?= =?UTF-8?q?mes=20for=20load=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/custom/custom_component.py | 4 ++-- tests/locust/names.txt | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/locust/names.txt diff --git a/src/backend/langflow/interface/custom/custom_component.py b/src/backend/langflow/interface/custom/custom_component.py index b1b1a2080..d470b080b 100644 --- a/src/backend/langflow/interface/custom/custom_component.py +++ b/src/backend/langflow/interface/custom/custom_component.py @@ -169,7 +169,7 @@ class CustomComponent(Component, extra=Extra.allow): return validate.create_function(self.code, self.function_entrypoint_name) def load_flow(self, flow_id: str, tweaks: Optional[dict] = None) -> Any: - from langflow.processing.process import build_sorted_vertices_with_caching + from langflow.processing.process import build_sorted_vertices from langflow.processing.process import process_tweaks db_manager = get_db_manager() @@ -179,7 +179,7 @@ class CustomComponent(Component, extra=Extra.allow): raise ValueError(f"Flow {flow_id} not found") if tweaks: graph_data = process_tweaks(graph_data=graph_data, tweaks=tweaks) - return build_sorted_vertices_with_caching(graph_data) + return build_sorted_vertices(graph_data) def list_flows(self, *, get_session: Optional[Callable] = None) -> List[Flow]: get_session = get_session or session_getter diff --git a/tests/locust/names.txt b/tests/locust/names.txt new file mode 100644 index 000000000..555af7369 --- /dev/null +++ b/tests/locust/names.txt @@ -0,0 +1,5 @@ +Bob +Alice +John +Gabriel +Lily From 298e499f72605e8fa456e0fe97c05cc393986ad7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 18 Aug 2023 10:09:45 -0300 Subject: [PATCH 058/213] =?UTF-8?q?=F0=9F=9A=80=20feat(base.Dockerfile):?= =?UTF-8?q?=20copy=20specific=20file=20'main.py'=20from=20src/backend/lang?= =?UTF-8?q?flow=20to=20improve=20build=20performance=20and=20reduce=20unne?= =?UTF-8?q?cessary=20copying=20=F0=9F=90=9B=20fix(base.Dockerfile):=20fix?= =?UTF-8?q?=20copying=20of=20src/backend=20directory=20to=20ensure=20all?= =?UTF-8?q?=20files=20are=20included=20in=20the=20build=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base.Dockerfile b/base.Dockerfile index 632a0fb46..c608e8c99 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -54,6 +54,7 @@ RUN apt-get update \ # deps for building python deps build-essential + # install poetry - respects $POETRY_VERSION & $POETRY_HOME # The --mount will mount the buildx cache directory to where # Poetry and Pip store their cache so that they can re-use it @@ -63,7 +64,7 @@ RUN --mount=type=cache,target=/root/.cache \ # copy project requirement files here to ensure they will be cached. WORKDIR $PYSETUP_PATH COPY poetry.lock pyproject.toml ./ -COPY ./src/backend ./src/backend +COPY ./src/backend/langflow/main.py ./src/backend/langflow/main.py # Copy README.md to the build context COPY README.md . # install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally @@ -82,6 +83,7 @@ WORKDIR $PYSETUP_PATH COPY --from=builder-base $POETRY_HOME $POETRY_HOME COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH +COPY ./src/backend ./src/backend # quicker install as runtime deps are already installed RUN --mount=type=cache,target=/root/.cache \ poetry install --with=dev --extras deploy From 3d84aace37eef716d7a21474245bc0ceb4fc8d03 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 13:24:47 -0300 Subject: [PATCH 059/213] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20imp?= =?UTF-8?q?ort=20missing=20dependencies=20to=20resolve=20NameError=20?= =?UTF-8?q?=E2=9C=A8=20feat(endpoints.py):=20add=20new=20endpoint=20to=20g?= =?UTF-8?q?et=20task=20status=20by=20task=20ID=20=F0=9F=94=A7=20fix(schema?= =?UTF-8?q?s.py):=20update=20ProcessResponse=20schema=20to=20use=20'id'=20?= =?UTF-8?q?instead=20of=20'session=5Fid'=20for=20consistency=20=E2=9C=A8?= =?UTF-8?q?=20feat(schemas.py):=20add=20new=20schema=20for=20TaskStatusRes?= =?UTF-8?q?ponse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 19 ++++++++++++++++--- src/backend/langflow/api/v1/schemas.py | 14 ++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 999f751d5..692438679 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -1,6 +1,7 @@ from http import HTTPStatus from typing import Annotated, Optional, Union + from langflow.services.cache.utils import save_uploaded_file from langflow.services.database.models.flow import Flow from langflow.processing.process import process_tweaks @@ -13,6 +14,7 @@ from langflow.interface.custom.custom_component import CustomComponent from langflow.api.v1.schemas import ( ProcessResponse, + TaskStatusResponse, UploadFileResponse, CustomComponentCode, ) @@ -29,6 +31,8 @@ from langflow.services.utils import get_session from langflow.worker import process_graph_cached_task from sqlmodel import Session +from celery.result import AsyncResult + # build router router = APIRouter(tags=["Base"]) @@ -96,16 +100,25 @@ async def process_flow( graph_data = process_tweaks(graph_data, tweaks) except Exception as exc: logger.error(f"Error processing tweaks: {exc}") - response, session_id = process_graph_cached_task.delay( + + task: "AsyncResult" = process_graph_cached_task.delay( graph_data, inputs, clear_cache, session_id - ).get() - return ProcessResponse(result=response, session_id=session_id) + ) + return ProcessResponse(result=task.state, id=task.id) except Exception as e: # Log stack trace logger.exception(e) raise HTTPException(status_code=500, detail=str(e)) from e +@router.get("/task/{task_id}/status", response_model=TaskStatusResponse) +async def get_task_status(task_id: str): + task = AsyncResult(task_id) + return TaskStatusResponse( + status=task.status, result=task.result if task.ready() else None + ) + + @router.post( "/upload/{flow_id}", response_model=UploadFileResponse, diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index 65bf64dca..f1a1ac902 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -46,8 +46,18 @@ class UpdateTemplateRequest(BaseModel): class ProcessResponse(BaseModel): """Process response schema.""" - result: dict - session_id: Optional[str] = None + result: Any + id: Optional[str] = None + + +# TaskStatusResponse( +# status=task.status, result=task.result if task.ready() else None +# ) +class TaskStatusResponse(BaseModel): + """Task status response schema.""" + + status: str + result: Optional[Any] = None class ChatMessage(BaseModel): From afcd1f847cdec8c52855e76de565114031f6b054 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 13:25:11 -0300 Subject: [PATCH 060/213] =?UTF-8?q?=F0=9F=94=A7=20chore(locustfile.py):=20?= =?UTF-8?q?update=20Locust=20test=20file=20to=20use=20FastHttpUser=20inste?= =?UTF-8?q?ad=20of=20HttpUser=20for=20improved=20performance=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(locustfile.py):=20update=20host=20URL=20to?= =?UTF-8?q?=20remove=20unnecessary=20colon=20in=20the=20port=20number=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(locustfile.py):=20refactor=20send=5Fname?= =?UTF-8?q?=5Fand=5Fcheck=20task=20to=20use=20a=20more=20efficient=20polli?= =?UTF-8?q?ng=20mechanism=20for=20task=20status=20=F0=9F=94=A7=20chore(loc?= =?UTF-8?q?ustfile.py):=20update=20flow=5Fid=20and=20session=5Fid=20genera?= =?UTF-8?q?tion=20logic=20to=20use=20more=20meaningful=20values=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(locustfile.py):=20refactor=20send=5Fname?= =?UTF-8?q?=5Fand=5Fcheck=20task=20to=20use=20a=20separate=20process=20fun?= =?UTF-8?q?ction=20for=20better=20code=20organization=20and=20readability?= =?UTF-8?q?=20=F0=9F=94=A7=20chore(locustfile.py):=20update=20payload2=20t?= =?UTF-8?q?o=20include=20a=20more=20descriptive=20text=20for=20the=20input?= =?UTF-8?q?=20=F0=9F=94=A7=20chore(locustfile.py):=20update=20assertion=20?= =?UTF-8?q?in=20send=5Fname=5Fand=5Fcheck=20task=20to=20check=20if=20the?= =?UTF-8?q?=20name=20is=20present=20in=20the=20result2=20response?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/locust/locustfile.py | 84 ++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/tests/locust/locustfile.py b/tests/locust/locustfile.py index 80b655f5b..a4d8b49c3 100644 --- a/tests/locust/locustfile.py +++ b/tests/locust/locustfile.py @@ -1,47 +1,69 @@ -from locust import HttpUser, task, between +from locust import FastHttpUser, task, between import random +import time +from rich import print -class NameTest(HttpUser): - host = "http://localhost:/api/v1" +class NameTest(FastHttpUser): + host = "http://localhost/api/v1" # make sure the port number is correct wait_time = between(1, 5) - # Read names from the file with open("names.txt", "r") as file: names = [line.strip() for line in file.readlines()] + def poll_task(self, task_id, sleep_time=1): + while True: + with self.rest( + "GET", + f"/task/{task_id}/status", + name="task_status", + ) as response: + status = response.js.get("status") + if status == "SUCCESS": + return response.js.get("result") + elif status in ["FAILURE", "REVOKED"]: + raise ValueError(f"Task failed with status: {status}") + time.sleep(sleep_time) + @task def send_name_and_check(self): - # Select a random name or in order from the list name = random.choice(self.names) - flow_id = "0bc439e4-539c-4b18-9813-92729326b171" # Replace with the appropriate flow ID - random_session_id_with_name = f"{name}-{random.randint(0, 1000000)}" - session_id = None - # First input + flow_id = ( + "e56cca5e-4bf3-4103-9a18-d55dcea135ec" # Replace with appropriate flow ID + ) + session_id = f"{name}-{time.time()}" + + def process(flow_id, payload): + task_id = None + print(f"Processing {payload}") + with self.rest( + "POST", f"/process/{flow_id}", json=payload, name="process" + ) as response: + if response.status_code != 200: + response.failure("Process call failed") + raise ValueError("Process call failed") + print(response.js) + task_id = response.js.get("id") + assert task_id, "Inner Task ID not found" + + assert task_id, "Task ID not found" + result, session_id = self.poll_task(task_id) + print(f"Result for {name}: {result}") + + return result, session_id + payload1 = { "inputs": {"text": f"Hello, My name is {name}"}, - "session_id": random_session_id_with_name, + "session_id": session_id, } - with self.client.post( - f"/process/{flow_id}", json=payload1, catch_response=True - ) as response: - if response.status_code != 200: - response.failure(f"Error: {response.json()}") - else: - response.success() + result1, session_id = process(flow_id, payload1) - session_id = response.json().get("session_id") - print(f"Session ID: {session_id}") + payload2 = { + "inputs": { + "text": "What is my name? Please, answer like this: Your name is " + }, + "session_id": session_id, + } + result2, session_id = process(flow_id, payload2) - if not session_id: - raise ValueError("Session ID not found") - - # Second input - payload2 = {"inputs": {"text": "What is my name?"}, "session_id": session_id} - with self.client.post( - f"/process/{flow_id}", json=payload2, catch_response=True - ) as response2: - if name not in response2.text: - response2.failure(f"Error {name} not in response: {response2.json()}") - else: - response2.success() + assert f"Your name is {name}" in result2, "Name not found in response" From 2d85ae9dd4fbc58324bf8017a08a281f5f6eb601 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 13:26:02 -0300 Subject: [PATCH 061/213] =?UTF-8?q?=F0=9F=9A=80=20feat(docker-compose.yml)?= =?UTF-8?q?:=20upgrade=20docker-compose=20version=20to=203.3=20for=20compa?= =?UTF-8?q?tibility=20with=20newer=20features=20and=20syntax?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 chore(docker-compose.yml): update backend.env file path to remove leading './' for consistency 🔧 chore(docker-compose.yml): remove explicit port mapping for backend service to use the default port 🔧 chore(docker-compose.yml): add deploy configuration for celery worker service to ensure only one replica is deployed 🔧 chore(docker-compose.yml): add prometheus and grafana services with necessary configurations and dependencies 📝 docs(prometheus.yml): add prometheus configuration file with scrape targets for prometheus and flower services --- deploy/docker-compose.yml | 33 +++++++++++++++++++++++++++++---- deploy/prometheus.yml | 11 +++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 deploy/prometheus.yml diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index fcdbf529b..09c994f78 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3" +version: "3.3" services: backend: @@ -10,9 +10,9 @@ services: - queue - db env_file: - - ./backend.env + - backend.env ports: - - "7860:7860" + - "7860" volumes: - ../:/app - ./startup-backend.sh:/startup-backend.sh # Ensure the paths match @@ -58,6 +58,8 @@ services: command: celery -A langflow.worker.celery_app worker --loglevel=INFO healthcheck: test: "exit 0" + deploy: + replicas: 1 flower: # user: your-non-root-user @@ -87,9 +89,32 @@ services: depends_on: backend: condition: service_healthy - env_file: - ./frontend.env ports: - "80:80" restart: on-failure + + prometheus: + image: prom/prometheus:v2.37.9 + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + command: + - "--config.file=/etc/prometheus/prometheus.yml" + ports: + - "9090:9090" + healthcheck: + test: "exit 0" + + grafana: + image: grafana/grafana:8.2.6 + depends_on: + prometheus: + condition: service_healthy + ports: + - "3000:3000" + volumes: + - grafana_data:/var/lib/grafana + +volumes: + grafana_data: diff --git a/deploy/prometheus.yml b/deploy/prometheus.yml new file mode 100644 index 000000000..1d53d5b5f --- /dev/null +++ b/deploy/prometheus.yml @@ -0,0 +1,11 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["prometheus:9090"] + - job_name: flower + static_configs: + - targets: ["flower:5555"] From bff329e278b209d098cf0e670f81bab961e29a29 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 13:26:29 -0300 Subject: [PATCH 062/213] =?UTF-8?q?=F0=9F=94=A7=20chore(flower.env):=20add?= =?UTF-8?q?=20FLOWER=5FUNAUTHENTICATED=5FAPI=3DTrue=20to=20allow=20unauthe?= =?UTF-8?q?nticated=20access=20to=20Flower=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/flower.env | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/flower.env b/deploy/flower.env index 845de63f5..652160629 100644 --- a/deploy/flower.env +++ b/deploy/flower.env @@ -4,6 +4,7 @@ LANGFLOW_REDIS_PORT=6379 LANGFLOW_REDIS_DB=0 LANGFLOW_REDIS_EXPIRE=3600 LANGFLOW_REDIS_PASSWORD= +FLOWER_UNAUTHENTICATED_API=True BROKER_URL=redis://queue:6379/0 RESULT_BACKEND=redis://queue:6379/0 C_FORCE_ROOT="true # ! Only for development" \ No newline at end of file From 9baf588e5e274bf00f1de477c55127d991eb2d30 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 23:38:47 -0300 Subject: [PATCH 063/213] =?UTF-8?q?=F0=9F=94=A5=20refactor(nginx.conf):=20?= =?UTF-8?q?remove=20redundant=20code=20for=20proxying=20/api/v1/=20and=20/?= =?UTF-8?q?health=20routes=20to=20backend=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/nginx.conf | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/frontend/nginx.conf b/src/frontend/nginx.conf index d069a206d..cabf31c7a 100644 --- a/src/frontend/nginx.conf +++ b/src/frontend/nginx.conf @@ -15,21 +15,6 @@ server { try_files $uri $uri/ /index.html =404; } - location ~ ^/api/v1/ { # Matching the /api/v1/ route - proxy_pass __BACKEND_URL__; # URL of your backend service - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - } - location /health { # Matching the /health route - proxy_pass __BACKEND_URL__; # URL of your backend service - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - } + include /etc/nginx/extra-conf.d/*.conf; } From 6ffb994e53f8414103c9f907c02174a7be0e6c37 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 23:39:19 -0300 Subject: [PATCH 064/213] =?UTF-8?q?=F0=9F=93=A6=20chore(docker-compose.ove?= =?UTF-8?q?rride.yml):=20add=20docker-compose.override.yml=20file=20to=20c?= =?UTF-8?q?onfigure=20Traefik=20proxy=20and=20services=20for=20local=20dev?= =?UTF-8?q?elopment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a new file `docker-compose.override.yml` to the `deploy` directory. The file contains configuration for the Traefik proxy and services used for local development. The configuration includes: - Enabling Docker in Traefik to read labels from Docker services - Adding a constraint to only use services with a specific label for this stack - Disabling the exposure of all Docker services by default - Enabling the access log and Traefik log - Enabling the Traefik Dashboard and API in insecure mode for local development The file also includes configuration for the following services: - `proxy`: Configured to listen on ports 80 and 8090, with labels for routing and load balancing - `pgadmin`: Configured to listen on port 5050 - `flower`: Configured to listen on port 5555 - `backend`: Configured with labels for routing and load balancing on paths `/api/v1`, `/docs`, and `/health` - `frontend`: Configured with labels for routing and load balancing on the root path The `traefik-public` network is also defined for use by the Traefik proxy. This file is intended to be used as an override for the main `docker-compose.yml` file during local development. 🐳 chore(docker-compose.yml): add Traefik reverse proxy configuration for services 🔧 fix(docker-compose.yml): fix path to startup-backend.sh script 🚀 feat(docker-compose.yml): add support for deploying backend, celery worker, flower, frontend, prometheus, and grafana services 🔧 fix(docker-compose.yml): fix volume configuration for db service 🔧 fix(docker-compose.yml): fix network configuration for pgadmin service 🔧 fix(docker-compose.yml): fix network configuration for traefik proxy service 🔧 fix(docker-compose.yml): fix network configuration for flower service 🔧 fix(docker-compose.yml): fix network configuration for frontend service 🔧 fix(docker-compose.yml): fix network configuration for prometheus service 🔧 fix(docker-compose.yml): fix network configuration for grafana service --- deploy/docker-compose.override.yml | 55 +++++++++++ deploy/docker-compose.yml | 153 ++++++++++++++++++++++++++--- 2 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 deploy/docker-compose.override.yml diff --git a/deploy/docker-compose.override.yml b/deploy/docker-compose.override.yml new file mode 100644 index 000000000..eda492a24 --- /dev/null +++ b/deploy/docker-compose.override.yml @@ -0,0 +1,55 @@ +version: "3.3" +services: + proxy: + ports: + - "80:80" + - "8090:8080" + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker + # Add a constraint to only use services with the label for this stack + # from the env var TRAEFIK_TAG + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG?Variable not set}`) + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Disable Docker Swarm mode for local development + # - --providers.docker.swarmmode + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + # Enable the Dashboard and API in insecure mode for local development + - --api.insecure=true + labels: + - traefik.enable=true + - traefik.http.routers.${STACK_NAME?Variable not set}-traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`) + - traefik.http.services.${STACK_NAME?Variable not set}-traefik-public.loadbalancer.server.port=80 + + pgadmin: + ports: + - "5050:5050" + + flower: + ports: + - "5555:5555" + + backend: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=7860 + + frontend: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) + - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 + +networks: + traefik-public: + # For local dev, don't expect an external Traefik network + external: false diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 09c994f78..e35424c03 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,7 +1,73 @@ version: "3.3" services: + proxy: + image: traefik:v2.2 + networks: + - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} + - default + volumes: + - /var/run/docker.sock:/var/run/docker.sock + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker + # Add a constraint to only use services with the label for this stack + # from the env var TRAEFIK_TAG + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG?Variable not set}`) + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + deploy: + placement: + constraints: + - node.role == manager + labels: + # Enable Traefik for this service, to make it available in the public network + - traefik.enable=true + # Use the traefik-public network (declared below) + - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK?Variable not set} + # Use the custom label "traefik.constraint-label=traefik-public" + # This public Traefik will only use services with this label + - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG?Variable not set} + # traefik-http set up only to use the middleware to redirect to https + - traefik.http.middlewares.${STACK_NAME?Variable not set}-https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.${STACK_NAME?Variable not set}-https-redirect.redirectscheme.permanent=true + # Handle host with and without "www" to redirect to only one of them + # Uses environment variable DOMAIN + # To disable www redirection remove the Host() you want to discard, here and + # below for HTTPS + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.entrypoints=http + # traefik-https the actual router using HTTPS + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.entrypoints=https + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.tls=true + # Use the "le" (Let's Encrypt) resolver created below + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.tls.certresolver=le + # Define the port inside of the Docker service to use + - traefik.http.services.${STACK_NAME?Variable not set}-proxy.loadbalancer.server.port=80 + # Handle domain with and without "www" to redirect to only one + # To disable www redirection remove the next line + - traefik.http.middlewares.${STACK_NAME?Variable not set}-www-redirect.redirectregex.regex=^https?://(www.)?(${DOMAIN?Variable not set})/(.*) + # Redirect a domain with www to non-www + # To disable it remove the next line + - traefik.http.middlewares.${STACK_NAME?Variable not set}-www-redirect.redirectregex.replacement=https://${DOMAIN?Variable not set}/$${3} + # Redirect a domain without www to www + # To enable it remove the previous line and uncomment the next + # - traefik.http.middlewares.${STACK_NAME}-www-redirect.redirectregex.replacement=https://www.${DOMAIN}/$${3} + # Middleware to redirect www, to disable it remove the next line + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-https.middlewares=${STACK_NAME?Variable not set}-www-redirect + # Middleware to redirect www, and redirect HTTP to HTTPS + # to disable www redirection remove the section: ${STACK_NAME?Variable not set}-www-redirect, + - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.middlewares=${STACK_NAME?Variable not set}-www-redirect,${STACK_NAME?Variable not set}-https-redirect + backend: + image: "langflow:${TAG-latest}" build: context: ../ dockerfile: base.Dockerfile @@ -19,24 +85,50 @@ services: command: /startup-backend.sh # Fixed the path healthcheck: test: "exit 0" + deploy: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=7860 db: image: postgres:15.4 + volumes: + - app-db-data:/var/lib/postgresql/data/pgdata env_file: - - ./db.env - ports: - - "5432:5432" + - db.env + environment: + - PGDATA=/var/lib/postgresql/data/pgdata + deploy: + placement: + constraints: + - node.labels.${STACK_NAME?Variable not set}.app-db-data == true pgadmin: image: dpage/pgadmin4 + networks: + - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} + - default env_file: - ./pgadmin.env - ports: - - "5050:80" depends_on: - db volumes: - ./pgadmin:/var/lib/pgadmin + deploy: + labels: + - traefik.enable=true + - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK?Variable not set} + - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-http.rule=Host(`pgadmin.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-http.entrypoints=http + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-http.middlewares=${STACK_NAME?Variable not set}-https-redirect + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.rule=Host(`pgadmin.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.entrypoints=https + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.tls=true + - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.tls.certresolver=le + - traefik.http.services.${STACK_NAME?Variable not set}-pgadmin.loadbalancer.server.port=5050 queue: image: redis:6.2.5 @@ -46,7 +138,7 @@ services: test: "exit 0" celeryworker: - # user: your-non-root-user + image: "langflow:${TAG-latest}" depends_on: queue: condition: service_healthy @@ -55,14 +147,14 @@ services: build: context: ../ dockerfile: base.Dockerfile - command: celery -A langflow.worker.celery_app worker --loglevel=INFO + command: celery -A langflow.worker.celery_app worker --loglevel=INFO --concurrency=1 -n lf-worker@%h healthcheck: test: "exit 0" deploy: replicas: 1 flower: - # user: your-non-root-user + image: mher/flower networks: - default depends_on: @@ -70,14 +162,25 @@ services: condition: service_healthy celeryworker: condition: service_healthy - build: - context: ../ - dockerfile: base.Dockerfile env_file: - ./flower.env - command: /bin/sh -c "celery -A langflow.worker.celery_app --broker=redis://$${LANGFLOW_REDIS_PASSWORD:+default:$${LANGFLOW_REDIS_PASSWORD}@}$${LANGFLOW_REDIS_HOST}:$${LANGFLOW_REDIS_PORT}/$${LANGFLOW_REDIS_DB} flower --port=5555" + environment: + - CELERY_BROKER_URL='redis://$${LANGFLOW_REDIS_PASSWORD:+default:$${LANGFLOW_REDIS_PASSWORD}@}$${LANGFLOW_REDIS_HOST}:$${LANGFLOW_REDIS_PORT}/$${LANGFLOW_REDIS_DB}' ports: - "5555:5555" + deploy: + labels: + - traefik.enable=true + - traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK?Variable not set} + - traefik.constraint-label=${TRAEFIK_PUBLIC_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-http.rule=Host(`flower.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-http.entrypoints=http + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-http.middlewares=${STACK_NAME?Variable not set}-https-redirect + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.rule=Host(`flower.${DOMAIN?Variable not set}`) + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.entrypoints=https + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.tls=true + - traefik.http.routers.${STACK_NAME?Variable not set}-flower-https.tls.certresolver=le + - traefik.http.services.${STACK_NAME?Variable not set}-flower.loadbalancer.server.port=5555 frontend: # user: your-non-root-user @@ -91,9 +194,13 @@ services: condition: service_healthy env_file: - ./frontend.env - ports: - - "80:80" restart: on-failure + deploy: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) + - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 prometheus: image: prom/prometheus:v2.37.9 @@ -105,6 +212,12 @@ services: - "9090:9090" healthcheck: test: "exit 0" + deploy: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-prometheus-http.rule=PathPrefix(`/metrics`) + - traefik.http.services.${STACK_NAME?Variable not set}-prometheus.loadbalancer.server.port=9090 grafana: image: grafana/grafana:8.2.6 @@ -115,6 +228,18 @@ services: - "3000:3000" volumes: - grafana_data:/var/lib/grafana + deploy: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-grafana-http.rule=PathPrefix(`/grafana`) + - traefik.http.services.${STACK_NAME?Variable not set}-grafana.loadbalancer.server.port=3000 volumes: grafana_data: + app-db-data: + +networks: + traefik-public: + # Allow setting it to false for testing + external: false # ${TRAEFIK_PUBLIC_NETWORK_IS_EXTERNAL-true} From d0911b3fd2bfbed36012ddfef4f0d2d41055c359 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 20 Aug 2023 23:41:21 -0300 Subject: [PATCH 065/213] =?UTF-8?q?=E2=9C=A8=20feat(.env.example):=20add?= =?UTF-8?q?=20example=20environment=20variables=20for=20domain,=20stack=20?= =?UTF-8?q?name,=20Traefik=20network,=20and=20Traefik=20tags=20to=20provid?= =?UTF-8?q?e=20a=20template=20for=20configuring=20the=20deployment=20envir?= =?UTF-8?q?onment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/.env.example | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 deploy/.env.example diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 000000000..4f8db086a --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,6 @@ +DOMAIN=localhost +STACK_NAME=langflow-stack + +TRAEFIK_PUBLIC_NETWORK=traefik-public +TRAEFIK_TAG=langflow-traefik +TRAEFIK_PUBLIC_TAG=traefik-public From fe65b634005ba61ebb06a32a5720eb5bae449abf Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 21 Aug 2023 08:19:12 -0300 Subject: [PATCH 066/213] =?UTF-8?q?=F0=9F=94=A7=20chore(deploy):=20update?= =?UTF-8?q?=20docker-compose=20version=20to=203.8=20for=20better=20compati?= =?UTF-8?q?bility=20and=20features=20=F0=9F=90=B3=20chore(deploy):=20updat?= =?UTF-8?q?e=20traefik=20image=20version=20to=20v3.0=20for=20improved=20fu?= =?UTF-8?q?nctionality=20and=20compatibility=20=F0=9F=94=A7=20chore(deploy?= =?UTF-8?q?):=20remove=20unnecessary=20port=20mappings=20for=20backend,=20?= =?UTF-8?q?queue,=20pgadmin,=20redis,=20prometheus,=20and=20grafana=20serv?= =?UTF-8?q?ices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.override.yml | 2 +- deploy/docker-compose.yml | 34 +++++++++++++++++------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/deploy/docker-compose.override.yml b/deploy/docker-compose.override.yml index eda492a24..d6984c959 100644 --- a/deploy/docker-compose.override.yml +++ b/deploy/docker-compose.override.yml @@ -1,4 +1,4 @@ -version: "3.3" +version: "3.8" services: proxy: ports: diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index e35424c03..78623ffa1 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,8 +1,8 @@ -version: "3.3" +version: "3.8" services: proxy: - image: traefik:v2.2 + image: traefik:v3.0 networks: - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default @@ -73,12 +73,14 @@ services: dockerfile: base.Dockerfile # user: your-non-root-user # Make sure your Dockerfile creates this user depends_on: - - queue - - db + db: + condition: service_healthy + queue: + condition: service_healthy env_file: - backend.env - ports: - - "7860" + # ports: + # - 7860 volumes: - ../:/app - ./startup-backend.sh:/startup-backend.sh # Ensure the paths match @@ -104,6 +106,8 @@ services: placement: constraints: - node.labels.${STACK_NAME?Variable not set}.app-db-data == true + healthcheck: + test: "exit 0" pgadmin: image: dpage/pgadmin4 @@ -113,7 +117,8 @@ services: env_file: - ./pgadmin.env depends_on: - - db + db: + condition: service_healthy volumes: - ./pgadmin:/var/lib/pgadmin deploy: @@ -132,8 +137,8 @@ services: queue: image: redis:6.2.5 - ports: - - "6379:6379" + # ports: + # - 6379:6379 healthcheck: test: "exit 0" @@ -166,8 +171,7 @@ services: - ./flower.env environment: - CELERY_BROKER_URL='redis://$${LANGFLOW_REDIS_PASSWORD:+default:$${LANGFLOW_REDIS_PASSWORD}@}$${LANGFLOW_REDIS_HOST}:$${LANGFLOW_REDIS_PORT}/$${LANGFLOW_REDIS_DB}' - ports: - - "5555:5555" + - FLOWER_PORT=5555 deploy: labels: - traefik.enable=true @@ -208,8 +212,8 @@ services: - ./prometheus.yml:/etc/prometheus/prometheus.yml command: - "--config.file=/etc/prometheus/prometheus.yml" - ports: - - "9090:9090" + # ports: + # - 9090:9090 healthcheck: test: "exit 0" deploy: @@ -224,8 +228,8 @@ services: depends_on: prometheus: condition: service_healthy - ports: - - "3000:3000" + # ports: + # - 3000:3000 volumes: - grafana_data:/var/lib/grafana deploy: From 517a48f885bc67ff20415b28127090425c0aaed3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 21 Aug 2023 13:45:41 -0300 Subject: [PATCH 067/213] First implementation of terraform setup --- deploy/scripts/terraform/.terraform.lock.hcl | 24 +++++++ deploy/scripts/terraform/main.tf | 69 +++++++++++++++++++ .../terraform/modules/docker-swarm/main.tf | 64 +++++++++++++++++ deploy/scripts/terraform/outputs.tf | 8 +++ deploy/scripts/terraform/variables.tf | 19 +++++ 5 files changed, 184 insertions(+) create mode 100644 deploy/scripts/terraform/.terraform.lock.hcl create mode 100644 deploy/scripts/terraform/main.tf create mode 100644 deploy/scripts/terraform/modules/docker-swarm/main.tf create mode 100644 deploy/scripts/terraform/outputs.tf create mode 100644 deploy/scripts/terraform/variables.tf diff --git a/deploy/scripts/terraform/.terraform.lock.hcl b/deploy/scripts/terraform/.terraform.lock.hcl new file mode 100644 index 000000000..3a247d856 --- /dev/null +++ b/deploy/scripts/terraform/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.13.1" + hashes = [ + "h1:T/p3Gp8uAvRk3BmBZI/B7JIUpxF+2DygvBsv2UvJK4w=", + "zh:0d107e410ecfbd5d2fb5ff9793f88e2ce03ae5b3bda4e3b772b5d146cdd859d8", + "zh:1080cf6a402939ec4ad393380f2ab2dfdc0e175903e08ed796aa22eb95868847", + "zh:300420d642c3ada48cfe633444eafa7bcd410cd6a8503de2384f14ac54dc3ce3", + "zh:4e0121014a8d6ef0b1ab4634877545737bb54e951340f1b67ffea8cd22b2d252", + "zh:59b401bbf95dc8c6bea58085ff286543380f176271251193eac09cb7fcf619b7", + "zh:5dfaf51e979131710ce8e1572e6012564e68c7c842e3d9caaaeb0fe6af15c351", + "zh:84bb75dafca056d7c3783be5185187fdd3294f902e9d72f7655f2efb5e066650", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:aa4e2b9f699d497041679bc05ca34ac21a5184298eb1813f35455b1883977910", + "zh:b51a4f08d84b071128df68a95cfa5114301a50bf8ab8e655dcf7e384e5bc6458", + "zh:bce284ac6ebb65053d9b703048e3157bf4175782ea9dbaec9f64dc2b6fcefb0c", + "zh:c748f78b79b354794098b06b18a02aefdb49be144dff8876c9204083980f7de0", + "zh:ee69d9aef5ca532392cdc26499096f3fa6e55f8622387f78194ebfaadb796aef", + "zh:ef561bee58e4976474bc056649095737fa3b2bcb74602032415d770bfc620c1f", + "zh:f696d8416c57c31f144d432779ba34764560a95937db3bb3dd2892a791a6d5a7", + ] +} diff --git a/deploy/scripts/terraform/main.tf b/deploy/scripts/terraform/main.tf new file mode 100644 index 000000000..2b84a4678 --- /dev/null +++ b/deploy/scripts/terraform/main.tf @@ -0,0 +1,69 @@ +provider "aws" { + region = "us-east-1" # Choose the region as needed +} + +module "docker-swarm" { + source = "./modules/docker-swarm" + key_name = aws_key_pair.swarm-key.key_name + vpc_id = aws_vpc.swarm-vpc.id + subnet_id = aws_subnet.swarm-subnet.id + security_group = aws_security_group.swarm-sg.id + instance_type = "t2.micro" # Choose the instance type as needed + manager_count = 1 + worker_count = 10 # This is the number of services in the docker-compose.yml file +} + +resource "aws_key_pair" "swarm-key" { + key_name = "swarm-key" + public_key = file("~/.ssh/id_rsa.pub") +} + +resource "aws_vpc" "swarm-vpc" { + cidr_block = "10.0.0.0/16" + enable_dns_support = true + enable_dns_hostnames = true +} + +resource "aws_subnet" "swarm-subnet" { + vpc_id = aws_vpc.swarm-vpc.id + cidr_block = "10.0.1.0/24" +} + +resource "aws_security_group" "swarm-sg" { + vpc_id = aws_vpc.swarm-vpc.id + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 2376 + to_port = 2377 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 7946 + to_port = 7946 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 4789 + to_port = 4789 + protocol = "udp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/deploy/scripts/terraform/modules/docker-swarm/main.tf b/deploy/scripts/terraform/modules/docker-swarm/main.tf new file mode 100644 index 000000000..851200691 --- /dev/null +++ b/deploy/scripts/terraform/modules/docker-swarm/main.tf @@ -0,0 +1,64 @@ +variable "key_name" {} +variable "vpc_id" {} +variable "subnet_id" {} +variable "security_group" {} +variable "instance_type" {} +variable "manager_count" {} +variable "worker_count" {} + + +resource "aws_instance" "manager" { + count = var.manager_count + ami = "ami-08a52ddb321b32a8c" # Amazon Linux 2 LTS + instance_type = var.instance_type + key_name = var.key_name + vpc_security_group_ids = [var.security_group] + subnet_id = var.subnet_id + associate_public_ip_address = true + + user_data = <<-EOT + #!/bin/bash + sudo yum update -y + sudo yum install -y docker + sudo service docker start + sudo usermod -a -G docker ec2-user + sudo chkconfig docker on + docker swarm init --advertise-addr $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4) + EOT + + tags = { + Name = "manager-${count.index}" + } +} + +resource "aws_instance" "worker" { + count = var.worker_count + ami = "ami-08a52ddb321b32a8c" # Amazon Linux 2 LTS + instance_type = var.instance_type + key_name = var.key_name + vpc_security_group_ids = [var.security_group] + subnet_id = var.subnet_id + associate_public_ip_address = true + + user_data = <<-EOT + #!/bin/bash + sudo yum update -y + sudo yum install -y docker + sudo service docker start + sudo usermod -a -G docker ec2-user + sudo chkconfig docker on + docker swarm join --token $(curl -s http://${aws_instance.manager.0.public_ip}:8080/token) ${aws_instance.manager.0.private_ip}:2377 + EOT + + tags = { + Name = "worker-${count.index}" + } +} + +output "manager_public_ips" { + value = aws_instance.manager[*].public_ip +} + +output "worker_public_ips" { + value = aws_instance.worker[*].public_ip +} diff --git a/deploy/scripts/terraform/outputs.tf b/deploy/scripts/terraform/outputs.tf new file mode 100644 index 000000000..30303fad9 --- /dev/null +++ b/deploy/scripts/terraform/outputs.tf @@ -0,0 +1,8 @@ +output "manager_public_ips" { + value = module.docker-swarm.manager_public_ips +} + +output "worker_public_ips" { + value = module.docker-swarm.worker_public_ips +} + diff --git a/deploy/scripts/terraform/variables.tf b/deploy/scripts/terraform/variables.tf new file mode 100644 index 000000000..e73528c88 --- /dev/null +++ b/deploy/scripts/terraform/variables.tf @@ -0,0 +1,19 @@ +variable "region" { + description = "The AWS region" + default = "us-east-1" +} + +variable "instance_type" { + description = "EC2 instance type" + default = "t2.micro" +} + +variable "manager_count" { + description = "Number of manager nodes" + default = 1 +} + +variable "worker_count" { + description = "Number of worker nodes" + default = 3 +} From 15f620047707c7d1f48b36ec6e5ef4d9a25384d9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 21 Aug 2023 13:46:35 -0300 Subject: [PATCH 068/213] =?UTF-8?q?=F0=9F=94=A7=20chore(locustfile.py):=20?= =?UTF-8?q?remove=20hardcoded=20flow=20ID=20and=20replace=20it=20with=20th?= =?UTF-8?q?e=20value=20from=20the=20FLOW=5FID=20environment=20variable=20f?= =?UTF-8?q?or=20flexibility=20and=20security?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/locust/locustfile.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/locust/locustfile.py b/tests/locust/locustfile.py index a4d8b49c3..b3e6b58b0 100644 --- a/tests/locust/locustfile.py +++ b/tests/locust/locustfile.py @@ -2,6 +2,7 @@ from locust import FastHttpUser, task, between import random import time from rich import print +import os class NameTest(FastHttpUser): @@ -28,9 +29,7 @@ class NameTest(FastHttpUser): @task def send_name_and_check(self): name = random.choice(self.names) - flow_id = ( - "e56cca5e-4bf3-4103-9a18-d55dcea135ec" # Replace with appropriate flow ID - ) + flow_id = os.getenv("FLOW_ID") session_id = f"{name}-{time.time()}" def process(flow_id, payload): From f4760a38a350197e5f3a7268d024438cb92900e9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 21 Aug 2023 13:47:02 -0300 Subject: [PATCH 069/213] =?UTF-8?q?=F0=9F=9A=80=20feat(build-push.sh):=20a?= =?UTF-8?q?dd=20script=20to=20build=20and=20push=20Docker=20images?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚀 feat(build.sh): add script to build Docker images --- deploy/scripts/build-push.sh | 11 +++++++++++ deploy/scripts/build.sh | 10 ++++++++++ 2 files changed, 21 insertions(+) create mode 100755 deploy/scripts/build-push.sh create mode 100755 deploy/scripts/build.sh diff --git a/deploy/scripts/build-push.sh b/deploy/scripts/build-push.sh new file mode 100755 index 000000000..287b99c67 --- /dev/null +++ b/deploy/scripts/build-push.sh @@ -0,0 +1,11 @@ + +#! /usr/bin/env sh + +# Exit in case of error +set -e + +TAG=${TAG?Variable not set} \ +FRONTEND_ENV=${FRONTEND_ENV-production} \ +sh ./scripts/build.sh + +docker-compose -f docker-compose.yml push \ No newline at end of file diff --git a/deploy/scripts/build.sh b/deploy/scripts/build.sh new file mode 100755 index 000000000..4d8c2743a --- /dev/null +++ b/deploy/scripts/build.sh @@ -0,0 +1,10 @@ +#! /usr/bin/env sh + +# Exit in case of error +set -e + +TAG=${TAG?Variable not set} \ +FRONTEND_ENV=${FRONTEND_ENV-production} \ +docker-compose \ +-f docker-compose.yml \ +build \ No newline at end of file From 6afaede7196178ebf41b9befbbf1b834d5888bc6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 21 Aug 2023 13:47:11 -0300 Subject: [PATCH 070/213] =?UTF-8?q?=E2=9C=A8=20feat(.gitignore):=20add=20.?= =?UTF-8?q?gitignore=20file=20to=20ignore=20Terraform=20related=20files=20?= =?UTF-8?q?and=20directories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/scripts/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 deploy/scripts/.gitignore diff --git a/deploy/scripts/.gitignore b/deploy/scripts/.gitignore new file mode 100644 index 000000000..c60caa840 --- /dev/null +++ b/deploy/scripts/.gitignore @@ -0,0 +1,5 @@ +# terraform +*.tfstate +*.tfstate.backup +*.tfvars +.terraform/ \ No newline at end of file From 3fe30671d9a8afcc4647d7bd6bee32054bd80ede Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Thu, 24 Aug 2023 17:06:23 +0200 Subject: [PATCH 071/213] fix(terraform): update subnet_id in docker-swarm module to use swarm-public-subnet instead of swarm-subnet for improved clarity feat(terraform): add aws_subnet resource for swarm-public-subnet to create a public subnet for the swarm cluster feat(terraform): add aws_internet_gateway resource to create an internet gateway for the swarm VPC feat(terraform): add aws_route_table resource to create a route table for the swarm VPC feat(terraform): add aws_route_table_association resource to associate the swarm-public-subnet with the public route table --- deploy/scripts/terraform/main.tf | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/deploy/scripts/terraform/main.tf b/deploy/scripts/terraform/main.tf index 2b84a4678..3bbd08fc1 100644 --- a/deploy/scripts/terraform/main.tf +++ b/deploy/scripts/terraform/main.tf @@ -6,7 +6,7 @@ module "docker-swarm" { source = "./modules/docker-swarm" key_name = aws_key_pair.swarm-key.key_name vpc_id = aws_vpc.swarm-vpc.id - subnet_id = aws_subnet.swarm-subnet.id + subnet_id = aws_subnet.swarm-public-subnet.id security_group = aws_security_group.swarm-sg.id instance_type = "t2.micro" # Choose the instance type as needed manager_count = 1 @@ -24,11 +24,34 @@ resource "aws_vpc" "swarm-vpc" { enable_dns_hostnames = true } -resource "aws_subnet" "swarm-subnet" { +resource "aws_subnet" "swarm-private-subnet" { vpc_id = aws_vpc.swarm-vpc.id cidr_block = "10.0.1.0/24" } +resource "aws_subnet" "swarm-public-subnet" { + vpc_id = aws_vpc.swarm-vpc.id + cidr_block = "10.0.2.0/24" +} + +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.swarm-vpc.id +} + +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.swarm-vpc.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } +} + +resource "aws_route_table_association" "public_subnet_asso" { + subnet_id = aws_subnet.swarm-public-subnet.id + route_table_id = aws_route_table.public_rt.id +} + resource "aws_security_group" "swarm-sg" { vpc_id = aws_vpc.swarm-vpc.id From d13771d8c92fbce94847cf5d8586460df30a7700 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Thu, 24 Aug 2023 18:48:44 +0200 Subject: [PATCH 072/213] =?UTF-8?q?=F0=9F=94=A7=20chore(terraform):=20upda?= =?UTF-8?q?te=20security=20group=20rules=20to=20allow=20ingress=20and=20eg?= =?UTF-8?q?ress=20traffic=20on=20port=208080=20=F0=9F=94=A7=20chore(terraf?= =?UTF-8?q?orm):=20modify=20user=20data=20script=20for=20manager=20instanc?= =?UTF-8?q?e=20to=20fetch=20instance=20metadata=20with=20token=20and=20sta?= =?UTF-8?q?rt=20a=20netcat=20server=20on=20port=208080=20to=20provide=20jo?= =?UTF-8?q?in=20token=20=F0=9F=94=A7=20chore(terraform):=20modify=20user?= =?UTF-8?q?=20data=20script=20for=20worker=20instance=20to=20fetch=20manag?= =?UTF-8?q?er=20IP=20and=20join=20the=20swarm=20using=20the=20join=20token?= =?UTF-8?q?=20obtained=20from=20the=20manager=20on=20port=208080?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/scripts/terraform/main.tf | 14 +++++++++ .../terraform/modules/docker-swarm/main.tf | 31 +++++++++++++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/deploy/scripts/terraform/main.tf b/deploy/scripts/terraform/main.tf index 3bbd08fc1..ea56a6448 100644 --- a/deploy/scripts/terraform/main.tf +++ b/deploy/scripts/terraform/main.tf @@ -89,4 +89,18 @@ resource "aws_security_group" "swarm-sg" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } + + ingress { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + cidr_blocks = [aws_subnet.swarm-public-subnet.cidr_block] + } + + egress { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + cidr_blocks = [aws_subnet.swarm-public-subnet.cidr_block] + } } diff --git a/deploy/scripts/terraform/modules/docker-swarm/main.tf b/deploy/scripts/terraform/modules/docker-swarm/main.tf index 851200691..ba1f873c3 100644 --- a/deploy/scripts/terraform/modules/docker-swarm/main.tf +++ b/deploy/scripts/terraform/modules/docker-swarm/main.tf @@ -20,10 +20,22 @@ resource "aws_instance" "manager" { #!/bin/bash sudo yum update -y sudo yum install -y docker + sudo yum install -y nc sudo service docker start sudo usermod -a -G docker ec2-user sudo chkconfig docker on - docker swarm init --advertise-addr $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4) + + # Fetch instance metadata with token + TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` + IP_ADDR=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/local-ipv4) + + docker swarm init --advertise-addr $IP_ADDR + + # Create a script to get the join token + echo 'docker swarm join-token worker -q' > get_token.sh + chmod +x get_token.sh + while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; ./get_token.sh; } | nc -l 8080; done & + EOT tags = { @@ -41,14 +53,15 @@ resource "aws_instance" "worker" { associate_public_ip_address = true user_data = <<-EOT - #!/bin/bash - sudo yum update -y - sudo yum install -y docker - sudo service docker start - sudo usermod -a -G docker ec2-user - sudo chkconfig docker on - docker swarm join --token $(curl -s http://${aws_instance.manager.0.public_ip}:8080/token) ${aws_instance.manager.0.private_ip}:2377 - EOT + #!/bin/bash + MANAGER_IP="${aws_instance.manager.0.private_ip}" + sudo yum update -y + sudo yum install -y docker + sudo service docker start + sudo usermod -a -G docker ec2-user + sudo chkconfig docker on + docker swarm join --token $(curl -s http://$MANAGER_IP:8080/token) $MANAGER_IP:2377 + EOT tags = { Name = "worker-${count.index}" From b3c0f3b8536dc81e2ec63ba5bb3b323416aefff3 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Fri, 25 Aug 2023 18:19:42 +0200 Subject: [PATCH 073/213] =?UTF-8?q?=F0=9F=94=A7=20chore(main.tf):=20add=20?= =?UTF-8?q?Terraform=20backend=20configuration=20for=20S3=20to=20store=20s?= =?UTF-8?q?tate=20remotely?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/scripts/terraform/main.tf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/deploy/scripts/terraform/main.tf b/deploy/scripts/terraform/main.tf index ea56a6448..76c8dcb58 100644 --- a/deploy/scripts/terraform/main.tf +++ b/deploy/scripts/terraform/main.tf @@ -1,3 +1,11 @@ +terraform { + backend "s3" { + bucket = "terraform-5fso81t4tn8z" + key = "backend/terraform.tfstate" + region = "us-east-1" + } +} + provider "aws" { region = "us-east-1" # Choose the region as needed } From d4ecb9896ddc1c09b107d012e031aa2af39b2264 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Mon, 28 Aug 2023 23:42:24 +0200 Subject: [PATCH 074/213] =?UTF-8?q?=F0=9F=94=A7=20chore(main.tf):=20add=20?= =?UTF-8?q?installation=20of=20git=20and=20docker-compose=20to=20improve?= =?UTF-8?q?=20deployment=20process=20=E2=9C=A8=20feat(main.tf):=20add=20cl?= =?UTF-8?q?oning=20of=20langflow=20repository=20and=20checkout=20to=20cele?= =?UTF-8?q?ry=20branch=20for=20specific=20project=20requirements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/.env.example | 35 +++++++++++++++++++ deploy/backend.env | 18 ---------- deploy/celeryworker.env | 8 ----- deploy/db.env | 3 -- deploy/flower.env | 10 ------ deploy/frontend.env | 3 -- deploy/pgadmin.env | 2 -- .../terraform/modules/docker-swarm/main.tf | 8 ++++- 8 files changed, 42 insertions(+), 45 deletions(-) delete mode 100644 deploy/backend.env delete mode 100644 deploy/celeryworker.env delete mode 100644 deploy/db.env delete mode 100644 deploy/flower.env delete mode 100644 deploy/frontend.env delete mode 100644 deploy/pgadmin.env diff --git a/deploy/.env.example b/deploy/.env.example index 4f8db086a..37af8772a 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -4,3 +4,38 @@ STACK_NAME=langflow-stack TRAEFIK_PUBLIC_NETWORK=traefik-public TRAEFIK_TAG=langflow-traefik TRAEFIK_PUBLIC_TAG=traefik-public + +# Database configuration +DB_USER=langflow +DB_PASSWORD=langflow +DB_HOST=db +DB_PORT=5432 +DB_NAME=langflow + +# Logging configuration +LOG_LEVEL=debug + +# DB configuration +POSTGRES_USER=langflow +POSTGRES_PASSWORD=langflow +POSTGRES_DB=langflow + +# Flower configuration +LANGFLOW_CACHE_TYPE=redis +LANGFLOW_REDIS_HOST=queue +LANGFLOW_REDIS_PORT=6379 +LANGFLOW_REDIS_DB=0 +LANGFLOW_REDIS_EXPIRE=3600 +LANGFLOW_REDIS_PASSWORD= +FLOWER_UNAUTHENTICATED_API=True +BROKER_URL=redis://queue:6379/0 +RESULT_BACKEND=redis://queue:6379/0 +C_FORCE_ROOT="true # ! Only for development" + +# Frontend configuration +VITE_PROXY_TARGET=http://backend:7860/api/ +BACKEND_URL=http://backend:7860 + +# PGAdmin configuration +PGADMIN_DEFAULT_EMAIL=admin@admin.com +PGADMIN_DEFAULT_PASSWORD=admin diff --git a/deploy/backend.env b/deploy/backend.env deleted file mode 100644 index d9cbdc1dc..000000000 --- a/deploy/backend.env +++ /dev/null @@ -1,18 +0,0 @@ -# Database configuration -DB_USER=langflow -DB_PASSWORD=langflow -DB_HOST=db -DB_PORT=5432 -DB_NAME=langflow - -# Logging configuration -LOG_LEVEL=debug - -# Cache configuration -LANGFLOW_CACHE_TYPE=redis - -# Redis configuration -LANGFLOW_REDIS_HOST=queue -LANGFLOW_REDIS_PORT=6379 -LANGFLOW_REDIS_DB=0 -LANGFLOW_REDIS_EXPIRE=3600 \ No newline at end of file diff --git a/deploy/celeryworker.env b/deploy/celeryworker.env deleted file mode 100644 index bbc6aa8c1..000000000 --- a/deploy/celeryworker.env +++ /dev/null @@ -1,8 +0,0 @@ -LANGFLOW_CACHE_TYPE=redis -LANGFLOW_REDIS_HOST=queue -LANGFLOW_REDIS_PORT=6379 -LANGFLOW_REDIS_DB=0 -LANGFLOW_REDIS_EXPIRE=3600 -BROKER_URL=redis://queue:6379/0 -RESULT_BACKEND=redis://queue:6379/0 -C_FORCE_ROOT="true # ! Only for development" diff --git a/deploy/db.env b/deploy/db.env deleted file mode 100644 index 64e9ce762..000000000 --- a/deploy/db.env +++ /dev/null @@ -1,3 +0,0 @@ -POSTGRES_USER=langflow -POSTGRES_PASSWORD=langflow -POSTGRES_DB=langflow diff --git a/deploy/flower.env b/deploy/flower.env deleted file mode 100644 index 652160629..000000000 --- a/deploy/flower.env +++ /dev/null @@ -1,10 +0,0 @@ -LANGFLOW_CACHE_TYPE=redis -LANGFLOW_REDIS_HOST=queue -LANGFLOW_REDIS_PORT=6379 -LANGFLOW_REDIS_DB=0 -LANGFLOW_REDIS_EXPIRE=3600 -LANGFLOW_REDIS_PASSWORD= -FLOWER_UNAUTHENTICATED_API=True -BROKER_URL=redis://queue:6379/0 -RESULT_BACKEND=redis://queue:6379/0 -C_FORCE_ROOT="true # ! Only for development" \ No newline at end of file diff --git a/deploy/frontend.env b/deploy/frontend.env deleted file mode 100644 index ca56a25a7..000000000 --- a/deploy/frontend.env +++ /dev/null @@ -1,3 +0,0 @@ - -VITE_PROXY_TARGET=http://backend:7860/api/ -BACKEND_URL=http://backend:7860 diff --git a/deploy/pgadmin.env b/deploy/pgadmin.env deleted file mode 100644 index 7ef01290b..000000000 --- a/deploy/pgadmin.env +++ /dev/null @@ -1,2 +0,0 @@ -PGADMIN_DEFAULT_EMAIL=admin@admin.com -PGADMIN_DEFAULT_PASSWORD=admin diff --git a/deploy/scripts/terraform/modules/docker-swarm/main.tf b/deploy/scripts/terraform/modules/docker-swarm/main.tf index ba1f873c3..bc05a4e24 100644 --- a/deploy/scripts/terraform/modules/docker-swarm/main.tf +++ b/deploy/scripts/terraform/modules/docker-swarm/main.tf @@ -20,7 +20,10 @@ resource "aws_instance" "manager" { #!/bin/bash sudo yum update -y sudo yum install -y docker + sudo yum install -y git sudo yum install -y nc + sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose sudo service docker start sudo usermod -a -G docker ec2-user sudo chkconfig docker on @@ -34,7 +37,10 @@ resource "aws_instance" "manager" { # Create a script to get the join token echo 'docker swarm join-token worker -q' > get_token.sh chmod +x get_token.sh - while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; ./get_token.sh; } | nc -l 8080; done & + timeout 5m bash -c "while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; ./get_token.sh; } | nc -l 8080; done" & + + git clone https://github.com/logspace-ai/langflow.git + git checkout celery EOT From 8faad709176499fae2a7b841596cc1a386724007 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:20:40 -0300 Subject: [PATCH 075/213] =?UTF-8?q?=E2=9C=A8=20feat(task):=20add=20TaskMan?= =?UTF-8?q?agerFactory=20class=20to=20create=20and=20configure=20a=20TaskM?= =?UTF-8?q?anager=20=F0=9F=90=9B=20fix(task):=20update=20get=5Ftask=5Fstat?= =?UTF-8?q?us=20function=20in=20TaskManager=20class=20to=20return=20the=20?= =?UTF-8?q?correct=20task=20status=20=F0=9F=94=A7=20chore(task):=20add=20A?= =?UTF-8?q?syncIOTaskResult=20class=20to=20handle=20asyncio=20tasks=20and?= =?UTF-8?q?=20get=20their=20status=20and=20result=20=F0=9F=94=A7=20chore(t?= =?UTF-8?q?ask):=20add=20get=5Fcelery=5Fworker=5Fstatus=20function=20to=20?= =?UTF-8?q?get=20the=20status=20of=20the=20Celery=20worker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/task/__init__.py | 0 src/backend/langflow/services/task/factory.py | 11 +++ src/backend/langflow/services/task/manager.py | 70 +++++++++++++++++++ src/backend/langflow/services/task/utils.py | 35 ++++++++++ 4 files changed, 116 insertions(+) create mode 100644 src/backend/langflow/services/task/__init__.py create mode 100644 src/backend/langflow/services/task/factory.py create mode 100644 src/backend/langflow/services/task/manager.py create mode 100644 src/backend/langflow/services/task/utils.py diff --git a/src/backend/langflow/services/task/__init__.py b/src/backend/langflow/services/task/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/services/task/factory.py b/src/backend/langflow/services/task/factory.py new file mode 100644 index 000000000..b21cb38cc --- /dev/null +++ b/src/backend/langflow/services/task/factory.py @@ -0,0 +1,11 @@ +from langflow.services.task.manager import TaskManager +from langflow.services.factory import ServiceFactory + + +class TaskManagerFactory(ServiceFactory): + def __init__(self): + super().__init__(TaskManager) + + def create(self): + # Here you would have logic to create and configure a TaskManager + return TaskManager() diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py new file mode 100644 index 000000000..6fc9dfa16 --- /dev/null +++ b/src/backend/langflow/services/task/manager.py @@ -0,0 +1,70 @@ +import asyncio +from typing import Any, Callable, Union +import logging + +from langflow.services.base import Service +from langflow.services.task.utils import AsyncIOTaskResult, get_celery_worker_status + +try: + from celery.result import AsyncResult + from langflow.worker import celery_app + + try: + status = get_celery_worker_status(celery_app) + except Exception as e: + logging.error(f"An error occurred: {e}") + status = {"availability": None} + USE_CELERY = status.get("availability") is not None +except ImportError: + USE_CELERY = False + + +class TaskManager(Service): + STATUS_PENDING = "PENDING" + STATUS_FINISHED = "FINISHED" + STATUS_UNKNOWN = "UNKNOWN" + name = "task_manager" + + def __init__(self): + self.tasks = {} # For storing asyncio tasks + self.celery_results = {} # For storing Celery AsyncResult instances + if USE_CELERY: + from langflow.worker import celery_app + + self.celery_app = celery_app + + else: + self.celery_app = None # To store the celery app if available + self.use_celery = USE_CELERY + + def launch_task( + self, + task_func: Callable[..., Any], + *args: Any, + **kwargs: Any, + ) -> Union[int, str]: + if USE_CELERY: + task = task_func.apply_async(args=args, kwargs=kwargs) + self.celery_results[task.id] = task + return task.id + else: + task = asyncio.create_task(task_func(*args, **kwargs)) + task_id = str(id(task)) + self.tasks[task_id] = AsyncIOTaskResult(task) + + def set_result(future): + try: + self.tasks[task_id] = AsyncIOTaskResult(future) + except Exception as e: + logging.error(f"An error occurred: {e}") + + task.add_done_callback(set_result) + return task_id + + # Update the get_task_status function in TaskManager class + def get_task( + self, task_id: Union[int, str] + ) -> Union[AsyncResult, AsyncIOTaskResult]: + if self.use_celery: + return AsyncResult(task_id, app=self.celery_app) + return self.tasks.get(task_id) diff --git a/src/backend/langflow/services/task/utils.py b/src/backend/langflow/services/task/utils.py new file mode 100644 index 000000000..7736c256a --- /dev/null +++ b/src/backend/langflow/services/task/utils.py @@ -0,0 +1,35 @@ +from asyncio import Task + + +class AsyncIOTaskResult: + def __init__(self, task: Task): + self._task = task + + @property + def status(self) -> str: + if self._task.done(): + return "FAILURE" if self._task.exception() is not None else "SUCCESS" + return "PENDING" + + @property + def result(self) -> any: + return self._task.result() if self._task.done() else None + + def ready(self) -> bool: + return self._task.done() + + +def get_celery_worker_status(app): + i = app.control.inspect() + availability = i.ping() + stats = i.stats() + registered_tasks = i.registered() + active_tasks = i.active() + scheduled_tasks = i.scheduled() + return { + "availability": availability, + "stats": stats, + "registered_tasks": registered_tasks, + "active_tasks": active_tasks, + "scheduled_tasks": scheduled_tasks, + } From 54cfca3dd6c990d0d1eebc01689366f82b9ebb87 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:20:55 -0300 Subject: [PATCH 076/213] =?UTF-8?q?=F0=9F=90=9B=20fix(api=5Fkey/crud.py):?= =?UTF-8?q?=20fix=20session=20concurrency=20issue=20when=20updating=20tota?= =?UTF-8?q?l=20uses=20and=20last=20used=20at=20fields?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/database/models/api_key/crud.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/services/database/models/api_key/crud.py b/src/backend/langflow/services/database/models/api_key/crud.py index abc84f108..d78da5a05 100644 --- a/src/backend/langflow/services/database/models/api_key/crud.py +++ b/src/backend/langflow/services/database/models/api_key/crud.py @@ -3,6 +3,8 @@ import secrets import threading from uuid import UUID from typing import List, Optional +from langflow.services.database.utils import session_getter +from langflow.services.utils import get_db_manager from sqlmodel import Session, select from langflow.services.database.models.api_key import ( ApiKey, @@ -63,9 +65,13 @@ def check_key(session: Session, api_key: str) -> Optional[ApiKey]: def update_total_uses(session, api_key: ApiKey): """Update the total uses and last used at.""" - api_key.total_uses += 1 - api_key.last_used_at = datetime.datetime.now(datetime.timezone.utc) - session.add(api_key) - session.commit() - session.refresh(api_key) + # This is running in a separate thread to avoid slowing down the request + # but session is not thread safe so we need to create a new session + db_manager = get_db_manager() + with session_getter(db_manager) as new_session: + api_key = new_session.get(ApiKey, api_key.id) + api_key.total_uses += 1 + api_key.last_used_at = datetime.datetime.now(datetime.timezone.utc) + new_session.add(api_key) + new_session.commit() return api_key From 7f2f8d1b0a61274e5edf7cb5d0af4aae23b53201 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:21:18 -0300 Subject: [PATCH 077/213] =?UTF-8?q?=F0=9F=94=A7=20chore(base.py):=20rename?= =?UTF-8?q?=20CACHE=20variable=20to=20CACHE=5FTYPE=20for=20better=20clarit?= =?UTF-8?q?y=20and=20consistency=20=F0=9F=94=A7=20chore(base.py):=20add=20?= =?UTF-8?q?LANGCHAIN=5FCACHE=20variable=20to=20specify=20the=20cache=20typ?= =?UTF-8?q?e=20for=20langchain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/settings/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py index 00cd2085f..140a1b634 100644 --- a/src/backend/langflow/services/settings/base.py +++ b/src/backend/langflow/services/settings/base.py @@ -37,9 +37,10 @@ class Settings(BaseSettings): DEV: bool = False DATABASE_URL: Optional[str] = None - CACHE: str = "InMemoryCache" + CACHE_TYPE: str = "memory" REMOVE_API_KEYS: bool = False COMPONENTS_PATH: List[str] = [] + LANGCHAIN_CACHE: str = "InMemoryCache" @validator("CONFIG_DIR", pre=True, allow_reuse=True) def set_langflow_dir(cls, value): From b6fa053291f16f5debef3d5358443aeb5deacc66 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:21:26 -0300 Subject: [PATCH 078/213] =?UTF-8?q?=F0=9F=90=9B=20fix(locustfile.py):=20up?= =?UTF-8?q?date=20host=20URL=20to=20include=20correct=20port=20number=20?= =?UTF-8?q?=E2=9C=A8=20feat(locustfile.py):=20add=20x-api-key=20header=20t?= =?UTF-8?q?o=20requests=20for=20authentication=20=F0=9F=90=9B=20fix(locust?= =?UTF-8?q?file.py):=20provide=20default=20value=20for=20FLOW=5FID=20envir?= =?UTF-8?q?onment=20variable=20to=20prevent=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/locust/locustfile.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/locust/locustfile.py b/tests/locust/locustfile.py index b3e6b58b0..765637a61 100644 --- a/tests/locust/locustfile.py +++ b/tests/locust/locustfile.py @@ -6,18 +6,24 @@ import os class NameTest(FastHttpUser): - host = "http://localhost/api/v1" # make sure the port number is correct + host = "http://localhost:7860/api/v1" # make sure the port number is correct wait_time = between(1, 5) with open("names.txt", "r") as file: names = [line.strip() for line in file.readlines()] + headers = { + # api-key + "x-api-key": "lf-a1EsRC75ybWiKRCRLyhX1R9rPlqQKYlnRQoZWysg6NM", + } + def poll_task(self, task_id, sleep_time=1): while True: with self.rest( "GET", f"/task/{task_id}/status", name="task_status", + headers=self.headers, ) as response: status = response.js.get("status") if status == "SUCCESS": @@ -29,14 +35,18 @@ class NameTest(FastHttpUser): @task def send_name_and_check(self): name = random.choice(self.names) - flow_id = os.getenv("FLOW_ID") + flow_id = os.getenv("FLOW_ID", "88989f6d-8da4-4d6f-8205-480693c36200") session_id = f"{name}-{time.time()}" def process(flow_id, payload): task_id = None print(f"Processing {payload}") with self.rest( - "POST", f"/process/{flow_id}", json=payload, name="process" + "POST", + f"/process/{flow_id}", + json=payload, + name="process", + headers=self.headers, ) as response: if response.status_code != 200: response.failure("Process call failed") From d508a1407cd2728d1a385ea5b8b2c2a42e351d1b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:21:40 -0300 Subject: [PATCH 079/213] =?UTF-8?q?=F0=9F=94=A8=20refactor(base.py):=20rem?= =?UTF-8?q?ove=20inheritance=20of=20Service=20class=20from=20BaseCacheMana?= =?UTF-8?q?ger=20to=20simplify=20class=20hierarchy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔨 refactor(factory.py): add type hinting for settings_manager parameter in create method of CacheManagerFactory 🔨 refactor(manager.py): remove inheritance of Service class from InMemoryCache and RedisCache to simplify class hierarchy --- src/backend/langflow/services/cache/base.py | 4 +--- src/backend/langflow/services/cache/factory.py | 6 +++++- src/backend/langflow/services/cache/manager.py | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/backend/langflow/services/cache/base.py b/src/backend/langflow/services/cache/base.py index f40cd6663..531f31284 100644 --- a/src/backend/langflow/services/cache/base.py +++ b/src/backend/langflow/services/cache/base.py @@ -1,9 +1,7 @@ import abc -from langflow.services.base import Service - -class BaseCacheManager(abc.ABC, Service): +class BaseCacheManager(abc.ABC): """ Abstract base class for a cache. """ diff --git a/src/backend/langflow/services/cache/factory.py b/src/backend/langflow/services/cache/factory.py index 018c0284a..4e497c5fd 100644 --- a/src/backend/langflow/services/cache/factory.py +++ b/src/backend/langflow/services/cache/factory.py @@ -1,13 +1,17 @@ from langflow.services.cache.manager import InMemoryCache, RedisCache, BaseCacheManager from langflow.services.factory import ServiceFactory from langflow.utils.logger import logger +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from langflow.services.settings.manager import SettingsManager class CacheManagerFactory(ServiceFactory): def __init__(self): super().__init__(BaseCacheManager) - def create(self, settings_manager): + def create(self, settings_manager: "SettingsManager"): # Here you would have logic to create and configure a CacheManager # based on the settings_service diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index a3f04518d..47cb82ecf 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -1,13 +1,14 @@ import threading import time from collections import OrderedDict +from langflow.services.base import Service from langflow.services.cache.base import BaseCacheManager import pickle -class InMemoryCache(BaseCacheManager): +class InMemoryCache(BaseCacheManager, Service): """ A simple in-memory cache using an OrderedDict. @@ -175,7 +176,7 @@ class InMemoryCache(BaseCacheManager): return f"InMemoryCache(max_size={self.max_size}, expiration_time={self.expiration_time})" -class RedisCache(BaseCacheManager): +class RedisCache(BaseCacheManager, Service): """ A Redis-based cache implementation. From 0cd90bf133c11a81bf6e7e67f88b50756a6c833b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:21:54 -0300 Subject: [PATCH 080/213] =?UTF-8?q?=F0=9F=90=9B=20fix(utils.py):=20update?= =?UTF-8?q?=20API=5FKEY=5FNAME=20from=20"api-key"=20to=20"x-api-key"=20to?= =?UTF-8?q?=20align=20with=20naming=20conventions=20and=20improve=20clarit?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/auth/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/services/auth/utils.py b/src/backend/langflow/services/auth/utils.py index 485968a38..7787684b6 100644 --- a/src/backend/langflow/services/auth/utils.py +++ b/src/backend/langflow/services/auth/utils.py @@ -17,7 +17,7 @@ from sqlmodel import Session oauth2_login = OAuth2PasswordBearer(tokenUrl="api/v1/login") -API_KEY_NAME = "api-key" +API_KEY_NAME = "x-api-key" api_key_query = APIKeyQuery( name=API_KEY_NAME, scheme_name="API key query", auto_error=False From 6418d5e56c40bb5056eaf8383fdc4d4af85d43cf Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:22:01 -0300 Subject: [PATCH 081/213] =?UTF-8?q?=F0=9F=94=80=20chore(utils.py):=20add?= =?UTF-8?q?=20type=20hints=20to=20imported=20modules=20for=20better=20code?= =?UTF-8?q?=20readability=20and=20maintainability=20=E2=9C=A8=20feat(utils?= =?UTF-8?q?.py):=20add=20get=5Ftask=5Fmanager()=20function=20to=20retrieve?= =?UTF-8?q?=20the=20TaskManager=20instance=20from=20the=20service=20manage?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/utils.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index f1b3a82aa..00c0571c5 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -5,6 +5,10 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from langflow.services.database.manager import DatabaseManager from langflow.services.settings.manager import SettingsManager + from langflow.services.cache.manager import BaseCacheManager + from langflow.services.session.manager import SessionManager + from langflow.services.task.manager import TaskManager + from sqlmodel import Session def get_settings_manager() -> "SettingsManager": @@ -15,14 +19,18 @@ def get_db_manager() -> "DatabaseManager": return service_manager.get(ServiceType.DATABASE_MANAGER) -def get_session(): +def get_session() -> "Session": db_manager = service_manager.get(ServiceType.DATABASE_MANAGER) yield from db_manager.get_session() -def get_cache_manager(): +def get_cache_manager() -> "BaseCacheManager": return service_manager.get(ServiceType.CACHE_MANAGER) -def get_session_manager(): +def get_session_manager() -> "SessionManager": return service_manager.get(ServiceType.SESSION_MANAGER) + + +def get_task_manager() -> "TaskManager": + return service_manager.get(ServiceType.TASK_MANAGER) From bd6655b0db0174a2a91c0b41fc274e962dc914d3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:22:14 -0300 Subject: [PATCH 082/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20handl?= =?UTF-8?q?e=20None=20service=20in=20teardown=20loop=20to=20prevent=20pote?= =?UTF-8?q?ntial=20errors=20=E2=9C=A8=20feat(manager.py):=20add=20support?= =?UTF-8?q?=20for=20task=20manager=20service=20in=20the=20service=20manage?= =?UTF-8?q?r=20initialization=20process=20=E2=9C=A8=20feat(schema.py):=20a?= =?UTF-8?q?dd=20task=20manager=20service=20type=20to=20the=20ServiceType?= =?UTF-8?q?=20enum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/manager.py | 11 +++++++++-- src/backend/langflow/services/schema.py | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index 60e3f3b66..b09bf0999 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -1,9 +1,10 @@ from langflow.services.schema import ServiceType -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from langflow.utils.logger import logger if TYPE_CHECKING: from langflow.services.factory import ServiceFactory + from langflow.services.base import Service class ServiceManager: @@ -12,7 +13,7 @@ class ServiceManager: """ def __init__(self): - self.services = {} + self.services: Dict[str, "Service"] = {} self.factories = {} self.dependencies = {} @@ -85,6 +86,8 @@ class ServiceManager: Teardown all the services. """ for service in self.services.values(): + if service is None: + continue logger.debug(f"Teardown service {service.name}") service.teardown() self.services = {} @@ -105,6 +108,7 @@ def initialize_services(): from langflow.services.settings import factory as settings_factory from langflow.services.session import factory as session_manager_factory from langflow.services.auth import factory as auth_factory + from langflow.services.task import factory as task_factory service_manager.register_factory(settings_factory.SettingsManagerFactory()) service_manager.register_factory( @@ -124,6 +128,9 @@ def initialize_services(): session_manager_factory.SessionManagerFactory(), dependencies=[ServiceType.CACHE_MANAGER], ) + service_manager.register_factory( + task_factory.TaskManagerFactory(), + ) # Test cache connection service_manager.get(ServiceType.CACHE_MANAGER) diff --git a/src/backend/langflow/services/schema.py b/src/backend/langflow/services/schema.py index 09b76d0a6..801e0cacd 100644 --- a/src/backend/langflow/services/schema.py +++ b/src/backend/langflow/services/schema.py @@ -13,3 +13,4 @@ class ServiceType(str, Enum): DATABASE_MANAGER = "database_manager" CHAT_MANAGER = "chat_manager" SESSION_MANAGER = "session_manager" + TASK_MANAGER = "task_manager" From e26d61fcfecbf89ac85442a61ab07079f7c4e9fa Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:22:30 -0300 Subject: [PATCH 083/213] =?UTF-8?q?=F0=9F=90=9B=20fix(endpoints.py):=20imp?= =?UTF-8?q?ort=20missing=20get=5Ftask=5Fmanager=20function=20to=20fix=20Na?= =?UTF-8?q?meError=20=E2=9C=A8=20feat(endpoints.py):=20add=20support=20for?= =?UTF-8?q?=20launching=20tasks=20using=20task=20manager=20to=20improve=20?= =?UTF-8?q?task=20management=20=F0=9F=90=9B=20fix(endpoints.py):=20change?= =?UTF-8?q?=20process=5Fgraph=5Fcached=5Ftask=20function=20call=20to=20use?= =?UTF-8?q?=20task=5Fmanager.launch=5Ftask=20to=20fix=20AttributeError=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(endpoints.py):=20change=20process=5Fflow=20f?= =?UTF-8?q?unction=20to=20use=20task=5Fmanager.get=5Ftask=20to=20fix=20Att?= =?UTF-8?q?ributeError=20=F0=9F=90=9B=20fix(endpoints.py):=20change=20get?= =?UTF-8?q?=5Ftask=5Fstatus=20function=20to=20use=20task=5Fmanager.get=5Ft?= =?UTF-8?q?ask=20to=20fix=20AttributeError=20=F0=9F=90=9B=20fix(process.py?= =?UTF-8?q?):=20change=20process=5Fgraph=5Fcached=20function=20to=20be=20a?= =?UTF-8?q?sync=20to=20fix=20TypeError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 25 +++++++++++++++------- src/backend/langflow/processing/process.py | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 4e8d3a582..988130ba7 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -5,9 +5,9 @@ from langflow.services.auth.utils import api_key_security, get_current_active_us from langflow.services.cache.utils import save_uploaded_file from langflow.services.database.models.flow import Flow -from langflow.processing.process import process_tweaks +from langflow.processing.process import process_graph_cached, process_tweaks from langflow.services.database.models.user.user import User -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_manager, get_task_manager from langflow.utils.logger import logger from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status import sqlalchemy as sa @@ -33,7 +33,6 @@ from langflow.services.utils import get_session from langflow.worker import process_graph_cached_task from sqlmodel import Session -from celery.result import AsyncResult # build router router = APIRouter(tags=["Base"]) @@ -129,11 +128,18 @@ async def process_flow( graph_data = process_tweaks(graph_data, tweaks) except Exception as exc: logger.error(f"Error processing tweaks: {exc}") - - task: "AsyncResult" = process_graph_cached_task.delay( - graph_data, inputs, clear_cache, session_id + task_manager = get_task_manager() + task_id = task_manager.launch_task( + process_graph_cached_task + if task_manager.use_celery + else process_graph_cached, + graph_data, + inputs, + clear_cache, + session_id, ) - return ProcessResponse(result=task.state, id=task.id) + task = task_manager.get_task(task_id) + return ProcessResponse(result=task.status, id=task_id) except sa.exc.StatementError as exc: # StatementError('(builtins.ValueError) badly formed hexadecimal UUID string') if "badly formed hexadecimal UUID string" in str(exc): @@ -158,7 +164,10 @@ async def process_flow( @router.get("/task/{task_id}/status", response_model=TaskStatusResponse) async def get_task_status(task_id: str): - task = AsyncResult(task_id) + task_manager = get_task_manager() + task = task_manager.get_task(task_id) + if task is None: + raise HTTPException(status_code=404, detail="Task not found") return TaskStatusResponse( status=task.status, result=task.result if task.ready() else None ) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 014e1db0b..e887c3a0b 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -173,7 +173,7 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): # return result, session_id -def process_graph_cached( +async def process_graph_cached( data_graph: Dict[str, Any], inputs: Optional[dict] = None, clear_cache=False, From f656b711731703101bee273ce09c9f9b1cca29d4 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 1 Sep 2023 17:22:40 -0300 Subject: [PATCH 084/213] =?UTF-8?q?=F0=9F=90=9B=20fix(utils.py):=20fix=20l?= =?UTF-8?q?ogger=20warning=20message=20to=20correctly=20display=20the=20CA?= =?UTF-8?q?CHE=5FTYPE=20or=20LANGCHAIN=5FCACHE=20variable=20instead=20of?= =?UTF-8?q?=20CACHE=20variable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/interface/utils.py b/src/backend/langflow/interface/utils.py index 822e32c35..e48ca4a7d 100644 --- a/src/backend/langflow/interface/utils.py +++ b/src/backend/langflow/interface/utils.py @@ -68,7 +68,7 @@ def setup_llm_caching(): try: set_langchain_cache(settings_manager.settings) except ImportError: - logger.warning(f"Could not import {settings_manager.settings.CACHE}. ") + logger.warning(f"Could not import {settings_manager.settings.CACHE_TYPE}. ") except Exception as exc: logger.warning(f"Could not setup LLM caching. Error: {exc}") @@ -79,7 +79,7 @@ def set_langchain_cache(settings): langchain_cache_type = os.getenv("LANGFLOW_LANGCHAIN_CACHE") cache_class = import_class( - f"langchain.cache.{langchain_cache_type or settings.CACHE}" + f"langchain.cache.{langchain_cache_type or settings.LANGCHAIN_CACHE}" ) logger.debug(f"Setting up LLM caching with {cache_class.__name__}") From e3038811553732975883dac92b38f28b2b566ae5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 4 Sep 2023 09:37:18 -0300 Subject: [PATCH 085/213] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20imp?= =?UTF-8?q?ort=20TaskManager=20from=20the=20correct=20module=20to=20fix=20?= =?UTF-8?q?import=20error=20=F0=9F=94=A7=20fix(endpoints.py):=20add=20miss?= =?UTF-8?q?ing=20import=20statement=20for=20TaskManager=20=F0=9F=94=A7=20f?= =?UTF-8?q?ix(endpoints.py):=20add=20missing=20import=20statement=20for=20?= =?UTF-8?q?sync=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 39 +++++++---- src/backend/langflow/services/task/manager.py | 65 +++++++------------ src/backend/langflow/services/task/utils.py | 21 ------ 3 files changed, 52 insertions(+), 73 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 988130ba7..048b4baae 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -33,6 +33,7 @@ from langflow.services.utils import get_session from langflow.worker import process_graph_cached_task from sqlmodel import Session +from langflow.services.task.manager import TaskManager # build router router = APIRouter(tags=["Base"]) @@ -97,7 +98,9 @@ async def process_flow( tweaks: Optional[dict] = None, clear_cache: Annotated[bool, Body(embed=True)] = False, # noqa: F821 session_id: Annotated[Union[None, str], Body(embed=True)] = None, # noqa: F821 + task_manager: "TaskManager" = Depends(get_task_manager), api_key_user: User = Depends(api_key_security), + sync: Annotated[bool, Body(embed=True)] = True, # noqa: F821 ): """ Endpoint to process an input with a given flow_id. @@ -128,18 +131,30 @@ async def process_flow( graph_data = process_tweaks(graph_data, tweaks) except Exception as exc: logger.error(f"Error processing tweaks: {exc}") - task_manager = get_task_manager() - task_id = task_manager.launch_task( - process_graph_cached_task - if task_manager.use_celery - else process_graph_cached, - graph_data, - inputs, - clear_cache, - session_id, - ) - task = task_manager.get_task(task_id) - return ProcessResponse(result=task.status, id=task_id) + if sync: + task_id, result = await task_manager.launch_and_await_task( + process_graph_cached_task + if task_manager.use_celery + else process_graph_cached, + graph_data, + inputs, + clear_cache, + session_id, + ) + task_result = result.result + session_id = result.session_id + else: + task_id, task = await task_manager.launch_task( + process_graph_cached_task + if task_manager.use_celery + else process_graph_cached, + graph_data, + inputs, + clear_cache, + session_id, + ) + task_result = task.status + return ProcessResponse(result=task_result, id=task_id, session_id=session_id) except sa.exc.StatementError as exc: # StatementError('(builtins.ValueError) badly formed hexadecimal UUID string') if "badly formed hexadecimal UUID string" in str(exc): diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py index 6fc9dfa16..c3e5e6444 100644 --- a/src/backend/langflow/services/task/manager.py +++ b/src/backend/langflow/services/task/manager.py @@ -1,12 +1,12 @@ -import asyncio from typing import Any, Callable, Union import logging from langflow.services.base import Service -from langflow.services.task.utils import AsyncIOTaskResult, get_celery_worker_status +from langflow.services.task.backends.anyio import AnyIOBackend +from langflow.services.task.backends.base import TaskBackend +from langflow.services.task.utils import get_celery_worker_status try: - from celery.result import AsyncResult from langflow.worker import celery_app try: @@ -20,51 +20,36 @@ except ImportError: class TaskManager(Service): - STATUS_PENDING = "PENDING" - STATUS_FINISHED = "FINISHED" - STATUS_UNKNOWN = "UNKNOWN" name = "task_manager" def __init__(self): - self.tasks = {} # For storing asyncio tasks - self.celery_results = {} # For storing Celery AsyncResult instances - if USE_CELERY: - from langflow.worker import celery_app - - self.celery_app = celery_app - - else: - self.celery_app = None # To store the celery app if available + self.backend = self.get_backend() self.use_celery = USE_CELERY - def launch_task( + def get_backend(self) -> TaskBackend: + if USE_CELERY: + from langflow.services.task.backends.celery import CeleryBackend + + return CeleryBackend() + return AnyIOBackend() + + # In your TaskManager class + async def launch_and_await_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any, - ) -> Union[int, str]: - if USE_CELERY: - task = task_func.apply_async(args=args, kwargs=kwargs) - self.celery_results[task.id] = task - return task.id - else: - task = asyncio.create_task(task_func(*args, **kwargs)) - task_id = str(id(task)) - self.tasks[task_id] = AsyncIOTaskResult(task) + ) -> Any: + if not self.use_celery: + return None, await task_func(*args, **kwargs) + task = task_func.apply(args=args, kwargs=kwargs) + result = task.get() + return task.id, result - def set_result(future): - try: - self.tasks[task_id] = AsyncIOTaskResult(future) - except Exception as e: - logging.error(f"An error occurred: {e}") + async def launch_task( + self, task_func: Callable[..., Any], *args: Any, **kwargs: Any + ) -> Union[str, str]: + return await self.backend.launch_task(task_func, *args, **kwargs) - task.add_done_callback(set_result) - return task_id - - # Update the get_task_status function in TaskManager class - def get_task( - self, task_id: Union[int, str] - ) -> Union[AsyncResult, AsyncIOTaskResult]: - if self.use_celery: - return AsyncResult(task_id, app=self.celery_app) - return self.tasks.get(task_id) + def get_task(self, task_id: Union[int, str]) -> Any: + return self.backend.get_task(task_id) diff --git a/src/backend/langflow/services/task/utils.py b/src/backend/langflow/services/task/utils.py index 7736c256a..412b33ae2 100644 --- a/src/backend/langflow/services/task/utils.py +++ b/src/backend/langflow/services/task/utils.py @@ -1,24 +1,3 @@ -from asyncio import Task - - -class AsyncIOTaskResult: - def __init__(self, task: Task): - self._task = task - - @property - def status(self) -> str: - if self._task.done(): - return "FAILURE" if self._task.exception() is not None else "SUCCESS" - return "PENDING" - - @property - def result(self) -> any: - return self._task.result() if self._task.done() else None - - def ready(self) -> bool: - return self._task.done() - - def get_celery_worker_status(app): i = app.control.inspect() availability = i.ping() From 8d484eb046ad415b6101ed06fd6582cc18459baa Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 4 Sep 2023 09:38:07 -0300 Subject: [PATCH 086/213] =?UTF-8?q?=E2=9C=A8=20feat(task=20backends):=20ad?= =?UTF-8?q?d=20support=20for=20AnyIO=20backend=20=F0=9F=93=A6=20chore(task?= =?UTF-8?q?=20backends):=20add=20base=20class=20for=20task=20backends=20?= =?UTF-8?q?=F0=9F=93=A6=20chore(task=20backends):=20add=20support=20for=20?= =?UTF-8?q?Celery=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/task/backends/__init__.py | 0 .../langflow/services/task/backends/anyio.py | 50 +++++++++++++++++++ .../langflow/services/task/backends/base.py | 14 ++++++ .../langflow/services/task/backends/celery.py | 18 +++++++ 4 files changed, 82 insertions(+) create mode 100644 src/backend/langflow/services/task/backends/__init__.py create mode 100644 src/backend/langflow/services/task/backends/anyio.py create mode 100644 src/backend/langflow/services/task/backends/base.py create mode 100644 src/backend/langflow/services/task/backends/celery.py diff --git a/src/backend/langflow/services/task/backends/__init__.py b/src/backend/langflow/services/task/backends/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/services/task/backends/anyio.py b/src/backend/langflow/services/task/backends/anyio.py new file mode 100644 index 000000000..68c449d4e --- /dev/null +++ b/src/backend/langflow/services/task/backends/anyio.py @@ -0,0 +1,50 @@ +from typing import Any, Callable, Tuple +import anyio +from langflow.services.task.backends.base import TaskBackend + + +class AnyIOTaskResult: + def __init__(self, scope): + self._scope = scope + self._status = "PENDING" + self._result = None + self._exception = None + + @property + def status(self) -> str: + if self._status == "DONE": + return "FAILURE" if self._exception is not None else "SUCCESS" + return self._status + + @property + def result(self) -> any: + return self._result + + def ready(self) -> bool: + return self._status == "DONE" + + async def run(self, func, *args, **kwargs): + try: + self._result = await func(*args, **kwargs) + except Exception as e: + self._exception = e + finally: + self._status = "DONE" + + +class AnyIOBackend(TaskBackend): + def __init__(self): + self.tasks = {} + + async def launch_task( + self, task_func: Callable[..., Any], *args: Any, **kwargs: Any + ) -> Tuple[str, AnyIOTaskResult]: # sourcery skip: remove-unnecessary-cast + async with anyio.create_task_group() as tg: + task_result = AnyIOTaskResult(tg) + await tg.spawn(task_result.run, task_func, *args, **kwargs) + task_id = str(id(task_result)) + self.tasks[task_id] = task_result + return task_id, task_result + + def get_task(self, task_id: int) -> AnyIOTaskResult: + return self.tasks.get(task_id) diff --git a/src/backend/langflow/services/task/backends/base.py b/src/backend/langflow/services/task/backends/base.py new file mode 100644 index 000000000..a05c760e7 --- /dev/null +++ b/src/backend/langflow/services/task/backends/base.py @@ -0,0 +1,14 @@ +from abc import ABC, abstractmethod +from typing import Any, Callable, Union + + +class TaskBackend(ABC): + @abstractmethod + def launch_task( + self, task_func: Callable[..., Any], *args: Any, **kwargs: Any + ) -> Union[int, str]: + pass + + @abstractmethod + def get_task(self, task_id: Union[int, str]) -> Any: + pass diff --git a/src/backend/langflow/services/task/backends/celery.py b/src/backend/langflow/services/task/backends/celery.py new file mode 100644 index 000000000..04e26159c --- /dev/null +++ b/src/backend/langflow/services/task/backends/celery.py @@ -0,0 +1,18 @@ +from typing import Any, Callable +from celery.result import AsyncResult +from langflow.services.task.backends.base import TaskBackend +from langflow.worker import celery_app + + +class CeleryBackend(TaskBackend): + def __init__(self): + self.celery_app = celery_app + + def launch_task( + self, task_func: Callable[..., Any], *args: Any, **kwargs: Any + ) -> str: + task = task_func.apply_async(args=args, kwargs=kwargs) + return task.id + + def get_task(self, task_id: str) -> AsyncResult: + return AsyncResult(task_id, app=self.celery_app) From 320bc96a7a1b5dda94ec12afaa61089d45545b97 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 4 Sep 2023 09:56:17 -0300 Subject: [PATCH 087/213] =?UTF-8?q?=F0=9F=94=A7=20fix(schemas.py):=20add?= =?UTF-8?q?=20session=5Fid=20field=20to=20ProcessResponse=20schema=20to=20?= =?UTF-8?q?support=20session-based=20processing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 fix(process.py): add Result dataclass to encapsulate result and session_id for session-based processing 🔧 fix(manager.py): remove unused method clear_session in SessionManager --- src/backend/langflow/api/v1/schemas.py | 1 + src/backend/langflow/processing/process.py | 9 ++++++++- src/backend/langflow/services/session/manager.py | 2 -- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index 28f351c75..835c9fcb0 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -52,6 +52,7 @@ class ProcessResponse(BaseModel): result: Any id: Optional[str] = None + session_id: Optional[str] = None # TaskStatusResponse( diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index e887c3a0b..da8996069 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass import json from pathlib import Path from langchain.schema import AgentAction @@ -173,6 +174,12 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): # return result, session_id +@dataclass +class Result: + result: Any + session_id: str + + async def process_graph_cached( data_graph: Dict[str, Any], inputs: Optional[dict] = None, @@ -191,7 +198,7 @@ async def process_graph_cached( session_id, data_graph, (langchain_object, artifacts) ) - return result, session_id + return Result(result, session_id) def load_flow_from_json( diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index 39cefba24..a131d15d1 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -34,5 +34,3 @@ class SessionManager(Service): def clear_session(self, session_id, data_graph): key = self.generate_key(session_id, data_graph) self.cache_manager.delete(key) - - # Additional methods to handle session-related logic From 7cb1dbf226b3f241bab46133fff5c1782143e26b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 4 Sep 2023 09:56:27 -0300 Subject: [PATCH 088/213] =?UTF-8?q?=F0=9F=90=9B=20fix(test=5Fendpoints.py)?= =?UTF-8?q?:=20change=20header=20key=20from=20'api-key'=20to=20'x-api-key'?= =?UTF-8?q?=20to=20align=20with=20API=20key=20header=20convention=20?= =?UTF-8?q?=E2=9C=A8=20feat(test=5Fendpoints.py):=20change=20mock=5Fproces?= =?UTF-8?q?s=5Fgraph=5Fcached=20function=20to=20be=20async=20and=20return?= =?UTF-8?q?=20a=20Result=20object=20instead=20of=20a=20tuple=20to=20improv?= =?UTF-8?q?e=20code=20readability=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_endpoints.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index cbb1eb08c..e48393bed 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -1,4 +1,5 @@ import uuid +from langflow.processing.process import Result from langflow.services.auth.utils import get_password_hash from langflow.services.database.models.api_key.api_key import ApiKey from langflow.services.utils import get_settings_manager @@ -105,8 +106,8 @@ def created_api_key(session, active_user): def test_process_flow_invalid_api_key(client, flow, monkeypatch): # Mock de process_graph_cached - def mock_process_graph_cached(*args, **kwargs): - return {}, "session_id_mock" + async def mock_process_graph_cached(*args, **kwargs): + return Result(result={}, session_id="session_id_mock") settings_manager = get_settings_manager() settings_manager.auth_settings.AUTO_LOGIN = False @@ -114,7 +115,7 @@ def test_process_flow_invalid_api_key(client, flow, monkeypatch): monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) - headers = {"api-key": "invalid_api_key"} + headers = {"x-api-key": "invalid_api_key"} post_data = { "inputs": {"key": "value"}, @@ -130,15 +131,15 @@ def test_process_flow_invalid_api_key(client, flow, monkeypatch): def test_process_flow_invalid_id(client, monkeypatch, created_api_key): - def mock_process_graph_cached(*args, **kwargs): - return {}, "session_id_mock" + async def mock_process_graph_cached(*args, **kwargs): + return Result(result={}, session_id="session_id_mock") from langflow.api.v1 import endpoints monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) api_key = created_api_key.api_key - headers = {"api-key": api_key} + headers = {"x-api-key": api_key} post_data = { "inputs": {"key": "value"}, @@ -163,13 +164,13 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k settings_manager = get_settings_manager() settings_manager.auth_settings.AUTO_LOGIN = False - def mock_process_graph_cached(*args, **kwargs): - return {}, "session_id_mock" + async def mock_process_graph_cached(*args, **kwargs): + return Result(result={}, session_id="session_id_mock") monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) api_key = created_api_key.api_key - headers = {"api-key": api_key} + headers = {"x-api-key": api_key} # Dummy POST data post_data = { @@ -185,8 +186,8 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k # Check the response assert response.status_code == 200, response.json() - assert response.json()["result"] == {} - assert response.json()["session_id"] == "session_id_mock" + assert response.json()["result"] == {}, response.json() + assert response.json()["session_id"] == "session_id_mock", response.json() def test_process_flow_fails_autologin_off(client, flow, monkeypatch): @@ -196,12 +197,12 @@ def test_process_flow_fails_autologin_off(client, flow, monkeypatch): settings_manager = get_settings_manager() settings_manager.auth_settings.AUTO_LOGIN = False - def mock_process_graph_cached(*args, **kwargs): - return {}, "session_id_mock" + async def mock_process_graph_cached(*args, **kwargs): + return Result(result={}, session_id="session_id_mock") monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) - headers = {"api-key": "api_key"} + headers = {"x-api-key": "api_key"} # Dummy POST data post_data = { From ae2e993680b9544420a89ca73d52bb422c81e9f8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 09:34:45 -0300 Subject: [PATCH 089/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20fix?= =?UTF-8?q?=20import=20statement=20to=20include=20Dict=20in=20typing=20mod?= =?UTF-8?q?ule=20=E2=9C=A8=20feat(manager.py):=20refactor=20check=5Ftable?= =?UTF-8?q?=20method=20in=20DatabaseManager=20class=20to=20use=20list=20co?= =?UTF-8?q?mprehension=20for=20improved=20readability=20and=20performance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/database/manager.py | 10 ++++------ src/backend/langflow/services/manager.py | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/backend/langflow/services/database/manager.py b/src/backend/langflow/services/database/manager.py index 7f8afab6f..a89db8395 100644 --- a/src/backend/langflow/services/database/manager.py +++ b/src/backend/langflow/services/database/manager.py @@ -107,12 +107,10 @@ class DatabaseManager(Service): # We will check that all models are in the database # and that the database is up to date with all columns sql_models = [models.Flow, models.User, models.ApiKey] - results = [] - for sql_model in sql_models: - results.append( - TableResults(sql_model.__tablename__, self.check_table(sql_model)) - ) - return results + return [ + TableResults(sql_model.__tablename__, self.check_table(sql_model)) + for sql_model in sql_models + ] def check_table(self, model): results = [] diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index 34c58c648..dc901730c 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -1,5 +1,5 @@ from langflow.services.schema import ServiceType -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from loguru import logger if TYPE_CHECKING: From f3d866f0b6a5dea6db264fc8aa43d6eba53b55ef Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 09:34:57 -0300 Subject: [PATCH 090/213] =?UTF-8?q?=F0=9F=90=9B=20fix(test=5Fendpoints.py)?= =?UTF-8?q?:=20fix=20import=20statements=20and=20add=20missing=20import=20?= =?UTF-8?q?for=20crud=20module=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20?= =?UTF-8?q?add=20mock=20function=20for=20update=5Ftotal=5Fuses=20to=20fix?= =?UTF-8?q?=20test=5Fprocess=5Fflow=5Finvalid=5Fapi=5Fkey=20=E2=9C=A8=20fe?= =?UTF-8?q?at(test=5Fendpoints.py):=20add=20mock=20function=20for=20update?= =?UTF-8?q?=5Ftotal=5Fuses=20to=20fix=20test=5Fprocess=5Fflow=5Fwithout=5F?= =?UTF-8?q?autologin=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20mock?= =?UTF-8?q?=20function=20for=20update=5Ftotal=5Fuses=20to=20fix=20test=5Fp?= =?UTF-8?q?rocess=5Fflow=5Ffails=5Fautologin=5Foff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_endpoints.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index e48393bed..376de6f4e 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -106,14 +106,20 @@ def created_api_key(session, active_user): def test_process_flow_invalid_api_key(client, flow, monkeypatch): # Mock de process_graph_cached - async def mock_process_graph_cached(*args, **kwargs): - return Result(result={}, session_id="session_id_mock") + from langflow.api.v1 import endpoints + from langflow.services.database.models.api_key import crud settings_manager = get_settings_manager() settings_manager.auth_settings.AUTO_LOGIN = False - from langflow.api.v1 import endpoints + + async def mock_process_graph_cached(*args, **kwargs): + return Result(result={}, session_id="session_id_mock") + + def mock_update_total_uses(*args, **kwargs): + return created_api_key monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) + monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses) headers = {"x-api-key": "invalid_api_key"} @@ -160,6 +166,7 @@ def test_process_flow_invalid_id(client, monkeypatch, created_api_key): def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_key): # Mock de process_graph_cached from langflow.api.v1 import endpoints + from langflow.services.database.models.api_key import crud settings_manager = get_settings_manager() settings_manager.auth_settings.AUTO_LOGIN = False @@ -167,7 +174,11 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k async def mock_process_graph_cached(*args, **kwargs): return Result(result={}, session_id="session_id_mock") + def mock_update_total_uses(*args, **kwargs): + return created_api_key + monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) + monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses) api_key = created_api_key.api_key headers = {"x-api-key": api_key} @@ -193,6 +204,7 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k def test_process_flow_fails_autologin_off(client, flow, monkeypatch): # Mock de process_graph_cached from langflow.api.v1 import endpoints + from langflow.services.database.models.api_key import crud settings_manager = get_settings_manager() settings_manager.auth_settings.AUTO_LOGIN = False @@ -200,7 +212,11 @@ def test_process_flow_fails_autologin_off(client, flow, monkeypatch): async def mock_process_graph_cached(*args, **kwargs): return Result(result={}, session_id="session_id_mock") + async def mock_update_total_uses(*args, **kwargs): + return created_api_key + monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) + monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses) headers = {"x-api-key": "api_key"} From f7e5a3d4221c33f2e8cec27da06ab3a858aedf8e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 11:53:30 -0300 Subject: [PATCH 091/213] =?UTF-8?q?=F0=9F=94=A7=20chore(test.yml):=20updat?= =?UTF-8?q?e=20POETRY=5FVERSION=20environment=20variable=20to=20"1.5.0"=20?= =?UTF-8?q?to=20use=20the=20latest=20version=20of=20Poetry=20=F0=9F=94=92?= =?UTF-8?q?=20chore(test.yml):=20add=20OPENAI=5FAPI=5FKEY=20environment=20?= =?UTF-8?q?variable=20to=20securely=20store=20the=20OpenAI=20API=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e009534c2..bc2e193c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ on: branches: [dev] env: - POETRY_VERSION: "1.4.0" + POETRY_VERSION: "1.5.0" jobs: build: @@ -16,6 +16,8 @@ jobs: matrix: python-version: - "3.10" + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} steps: - uses: actions/checkout@v3 - name: Install poetry From a6e9cce02f86c73cd1a36155e97b8023876313df Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 11:53:54 -0300 Subject: [PATCH 092/213] =?UTF-8?q?=F0=9F=90=9B=20fix(process.py):=20fix?= =?UTF-8?q?=20session=5Fid=20assignment=20logic=20to=20ensure=20it=20is=20?= =?UTF-8?q?generated=20if=20not=20provided?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 7ef59de47..8fe825e3d 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -188,6 +188,10 @@ async def process_graph_cached( ) -> Tuple[Any, str]: clear_caches_if_needed(clear_cache) session_manager = get_session_manager() + if session_id is None: + session_id = session_manager.generate_key( + session_id=session_id, data_graph=data_graph + ) # Load the graph using SessionManager langchain_object, artifacts = session_manager.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) From e1800ff682778eeb1ada5a60ef8378b9efdcf5f3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 11:54:53 -0300 Subject: [PATCH 093/213] =?UTF-8?q?=F0=9F=94=A7=20chore(api=5Fkey/crud.py)?= =?UTF-8?q?:=20remove=20unused=20imports=20and=20variables=20for=20cleaner?= =?UTF-8?q?=20code=20=F0=9F=90=9B=20fix(api=5Fkey/crud.py):=20update=5Ftot?= =?UTF-8?q?al=5Fuses=20function=20now=20uses=20the=20same=20session=20as?= =?UTF-8?q?=20the=20caller=20to=20avoid=20thread=20safety=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/database/models/api_key/crud.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/services/database/models/api_key/crud.py b/src/backend/langflow/services/database/models/api_key/crud.py index d78da5a05..756236018 100644 --- a/src/backend/langflow/services/database/models/api_key/crud.py +++ b/src/backend/langflow/services/database/models/api_key/crud.py @@ -3,8 +3,6 @@ import secrets import threading from uuid import UUID from typing import List, Optional -from langflow.services.database.utils import session_getter -from langflow.services.utils import get_db_manager from sqlmodel import Session, select from langflow.services.database.models.api_key import ( ApiKey, @@ -67,8 +65,8 @@ def update_total_uses(session, api_key: ApiKey): """Update the total uses and last used at.""" # This is running in a separate thread to avoid slowing down the request # but session is not thread safe so we need to create a new session - db_manager = get_db_manager() - with session_getter(db_manager) as new_session: + + with Session(session.get_bind()) as new_session: api_key = new_session.get(ApiKey, api_key.id) api_key.total_uses += 1 api_key.last_used_at = datetime.datetime.now(datetime.timezone.utc) From c68b9e39c784b6d3c37ccc0ae1c9eeb9fd935e7c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 11:55:19 -0300 Subject: [PATCH 094/213] =?UTF-8?q?=F0=9F=9A=80=20feat(session):=20add=20s?= =?UTF-8?q?ession=5Fid=5Fgenerator=20utility=20function=20to=20generate=20?= =?UTF-8?q?a=20random=20session=20ID=20=F0=9F=90=9B=20fix(session):=20gene?= =?UTF-8?q?rate=20a=205=20character=20session=20ID=20if=20session=5Fid=20i?= =?UTF-8?q?s=20None=20to=20ensure=20a=20unique=20key=20is=20created?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/session/manager.py | 5 +++++ src/backend/langflow/services/session/utils.py | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 src/backend/langflow/services/session/utils.py diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index a131d15d1..76ac42113 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -2,6 +2,8 @@ from langflow.interface.run import build_sorted_vertices from langflow.services.base import Service from langflow.services.cache.utils import compute_dict_hash +from langflow.services.session.utils import session_id_generator + class SessionManager(Service): name = "session_manager" @@ -25,6 +27,9 @@ class SessionManager(Service): def generate_key(self, session_id, data_graph): # Hash the JSON and combine it with the session_id to create a unique key json_hash = compute_dict_hash(data_graph) + if session_id is None: + # generate a 5 char session_id to concatenate with the json_hash + session_id = session_id_generator() return f"{session_id}{':' if session_id else ''}{json_hash}" def update_session(self, session_id, data_graph, value): diff --git a/src/backend/langflow/services/session/utils.py b/src/backend/langflow/services/session/utils.py new file mode 100644 index 000000000..374d85540 --- /dev/null +++ b/src/backend/langflow/services/session/utils.py @@ -0,0 +1,8 @@ +import random +import string + + +def session_id_generator(size=6): + return "".join( + random.SystemRandom().choices(string.ascii_uppercase + string.digits, k=size) + ) From a61b2bbe1dd468e5c029a9b53a6d58f945552136 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 11:55:39 -0300 Subject: [PATCH 095/213] =?UTF-8?q?=F0=9F=94=A7=20chore(conftest.py):=20im?= =?UTF-8?q?port=20orjson=20library=20for=20JSON=20handling=20=F0=9F=94=A7?= =?UTF-8?q?=20chore(conftest.py):=20add=20pytest=20fixture=20for=20loading?= =?UTF-8?q?=20JSON=20flow=20with=20prompt=20and=20history=20=F0=9F=94=A7?= =?UTF-8?q?=20chore(conftest.py):=20add=20pytest=20fixture=20for=20adding?= =?UTF-8?q?=20a=20flow=20with=20prompt=20and=20history=20to=20the=20databa?= =?UTF-8?q?se?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1d1fb9ac7..8df9e1655 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,8 +6,9 @@ from langflow.api.v1.flows import get_session from langflow.graph.graph.base import Graph from langflow.services.auth.utils import get_password_hash -from langflow.services.database.models.flow.flow import Flow +from langflow.services.database.models.flow.flow import Flow, FlowCreate from langflow.services.database.models.user.user import User, UserCreate +import orjson import pytest from fastapi.testclient import TestClient from httpx import AsyncClient @@ -29,6 +30,9 @@ def pytest_configure(): pytest.OPENAPI_EXAMPLE_PATH = ( Path(__file__).parent.absolute() / "data" / "Openapi.json" ) + pytest.BASIC_CHAT_WITH_PROMPT_AND_HISTORY = ( + Path(__file__).parent.absolute() / "data" / "BasicChatWithPromptAndHistory.json" + ) pytest.CODE_WITH_SYNTAX_ERROR = """ def get_text(): @@ -101,6 +105,12 @@ def json_flow(): return f.read() +@pytest.fixture +def json_flow_with_prompt_and_history(): + with open(pytest.BASIC_CHAT_WITH_PROMPT_AND_HISTORY, "r") as f: + return f.read() + + @pytest.fixture(name="session") def session_fixture(): engine = create_engine( @@ -208,3 +218,15 @@ def flow(client, json_flow: str, session, active_user): session.commit() return flow + + +@pytest.fixture +def added_flow(client, json_flow_with_prompt_and_history, logged_in_headers): + flow = orjson.loads(json_flow_with_prompt_and_history) + data = flow["data"] + flow = FlowCreate(name="Basic Chat", description="description", data=data) + response = client.post("api/v1/flows/", json=flow.dict(), headers=logged_in_headers) + assert response.status_code == 201 + assert response.json()["name"] == flow.name + assert response.json()["data"] == flow.data + return response.json() From 6309de1654a74e241adf0227185410a07ea89029 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 11:55:53 -0300 Subject: [PATCH 096/213] =?UTF-8?q?=F0=9F=9A=80=20feat:=20add=20Basic=20Ch?= =?UTF-8?q?at=20with=20Prompt=20and=20History=20(2)=20node?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ℹ️ This commit adds a new node called "Basic Chat with Prompt and History (2)" to the chat application. This node is responsible for creating a simple chat interface with a custom prompt template and a conversational memory buffer. The node configuration includes various properties such as callbacks, cache, client, max_retries, max_tokens, metadata, model_kwargs, model_name, n, openai_api_base, openai_api_key, openai_organization, openai_proxy, and more. This new node enhances the chat functionality by providing a more interactive and dynamic chat experience for users. 📝 chore(tests): add BasicChatwithPromptandHistory.json test data file for testing purposes 🔧 fix(test_endpoints.py): remove unused imports and trailing whitespace ✅ test(test_endpoints.py): add test for basic chat in process endpoint to verify correct response and session_id generation ✅ test(test_endpoints.py): add test for basic chat with different session_ids to verify correct response ✅ test(test_endpoints.py): add test for basic chat with two session_ids and names to verify correct response --- tests/data/BasicChatwithPromptandHistory.json | 1 + tests/test_endpoints.py | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/data/BasicChatwithPromptandHistory.json diff --git a/tests/data/BasicChatwithPromptandHistory.json b/tests/data/BasicChatwithPromptandHistory.json new file mode 100644 index 000000000..658ac0479 --- /dev/null +++ b/tests/data/BasicChatwithPromptandHistory.json @@ -0,0 +1 @@ +{"description":"A simple chat with a custom prompt template and conversational memory buffer","name":"Basic Chat with Prompt and History (2)","data":{"nodes":[{"width":384,"height":621,"id":"ChatOpenAI-vy7fV","type":"genericNode","position":{"x":170.87326389541306,"y":465.8628482073749},"data":{"type":"ChatOpenAI","node":{"template":{"callbacks":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"callbacks","advanced":false,"dynamic":false,"info":"","type":"langchain.callbacks.base.BaseCallbackHandler","list":true},"cache":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"cache","advanced":false,"dynamic":false,"info":"","type":"bool","list":false},"client":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"client","advanced":false,"dynamic":false,"info":"","type":"Any","list":false},"max_retries":{"required":false,"placeholder":"","show":false,"multiline":false,"value":6,"password":false,"name":"max_retries","advanced":false,"dynamic":false,"info":"","type":"int","list":false},"max_tokens":{"required":false,"placeholder":"","show":true,"multiline":false,"password":true,"name":"max_tokens","advanced":false,"dynamic":false,"info":"","type":"int","list":false,"value":""},"metadata":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"metadata","advanced":false,"dynamic":false,"info":"","type":"code","list":false},"model_kwargs":{"required":false,"placeholder":"","show":true,"multiline":false,"password":false,"name":"model_kwargs","advanced":true,"dynamic":false,"info":"","type":"code","list":false},"model_name":{"required":false,"placeholder":"","show":true,"multiline":false,"value":"gpt-3.5-turbo","password":false,"options":["gpt-3.5-turbo-0613","gpt-3.5-turbo","gpt-3.5-turbo-16k-0613","gpt-3.5-turbo-16k","gpt-4-0613","gpt-4-32k-0613","gpt-4","gpt-4-32k"],"name":"model_name","advanced":false,"dynamic":false,"info":"","type":"str","list":true},"n":{"required":false,"placeholder":"","show":false,"multiline":false,"value":1,"password":false,"name":"n","advanced":false,"dynamic":false,"info":"","type":"int","list":false},"openai_api_base":{"required":false,"placeholder":"","show":true,"multiline":false,"password":false,"name":"openai_api_base","display_name":"OpenAI API Base","advanced":false,"dynamic":false,"info":"\nThe base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.\n","type":"str","list":false},"openai_api_key":{"required":false,"placeholder":"","show":true,"multiline":false,"value":"","password":true,"name":"openai_api_key","display_name":"OpenAI API Key","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"openai_organization":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"openai_organization","display_name":"OpenAI Organization","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"openai_proxy":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"openai_proxy","display_name":"OpenAI Proxy","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"request_timeout":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"request_timeout","advanced":false,"dynamic":false,"info":"","type":"float","list":false,"value":60},"streaming":{"required":false,"placeholder":"","show":false,"multiline":false,"value":false,"password":false,"name":"streaming","advanced":false,"dynamic":false,"info":"","type":"bool","list":false},"tags":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"tags","advanced":false,"dynamic":false,"info":"","type":"str","list":true},"temperature":{"required":false,"placeholder":"","show":true,"multiline":false,"value":0.7,"password":false,"name":"temperature","advanced":false,"dynamic":false,"info":"","type":"float","list":false},"tiktoken_model_name":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"tiktoken_model_name","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"verbose":{"required":false,"placeholder":"","show":false,"multiline":false,"value":false,"password":false,"name":"verbose","advanced":false,"dynamic":false,"info":"","type":"bool","list":false},"_type":"ChatOpenAI"},"description":"`OpenAI` Chat large language models API.","base_classes":["ChatOpenAI","BaseChatModel","BaseLanguageModel","BaseLLM"],"display_name":"ChatOpenAI","documentation":"https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai"},"id":"ChatOpenAI-vy7fV","value":null},"selected":true,"dragging":false,"positionAbsolute":{"x":170.87326389541306,"y":465.8628482073749}},{"width":384,"height":307,"id":"LLMChain-UjBh1","type":"genericNode","position":{"x":1250.1806448178158,"y":588.4657451068704},"data":{"type":"LLMChain","node":{"template":{"callbacks":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"callbacks","advanced":false,"dynamic":false,"info":"","type":"langchain.callbacks.base.BaseCallbackHandler","list":true},"llm":{"required":true,"placeholder":"","show":true,"multiline":false,"password":false,"name":"llm","advanced":false,"dynamic":false,"info":"","type":"BaseLanguageModel","list":false},"memory":{"required":false,"placeholder":"","show":true,"multiline":false,"password":false,"name":"memory","advanced":false,"dynamic":false,"info":"","type":"BaseMemory","list":false},"output_parser":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"output_parser","advanced":false,"dynamic":false,"info":"","type":"BaseLLMOutputParser","list":false},"prompt":{"required":true,"placeholder":"","show":true,"multiline":false,"password":false,"name":"prompt","advanced":false,"dynamic":false,"info":"","type":"BasePromptTemplate","list":false},"llm_kwargs":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"llm_kwargs","advanced":false,"dynamic":false,"info":"","type":"code","list":false},"metadata":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"metadata","advanced":false,"dynamic":false,"info":"","type":"code","list":false},"output_key":{"required":true,"placeholder":"","show":true,"multiline":false,"value":"text","password":false,"name":"output_key","advanced":true,"dynamic":false,"info":"","type":"str","list":false},"return_final_only":{"required":false,"placeholder":"","show":false,"multiline":false,"value":true,"password":false,"name":"return_final_only","advanced":false,"dynamic":false,"info":"","type":"bool","list":false},"tags":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"tags","advanced":false,"dynamic":false,"info":"","type":"str","list":true},"verbose":{"required":false,"placeholder":"","show":false,"multiline":false,"value":false,"password":false,"name":"verbose","advanced":true,"dynamic":false,"info":"","type":"bool","list":false},"_type":"LLMChain"},"description":"Chain to run queries against LLMs.","base_classes":["LLMChain","Chain","function"],"display_name":"LLMChain","documentation":"https://python.langchain.com/docs/modules/chains/foundational/llm_chain"},"id":"LLMChain-UjBh1","value":null},"selected":false,"positionAbsolute":{"x":1250.1806448178158,"y":588.4657451068704},"dragging":false},{"width":384,"height":273,"id":"PromptTemplate-5Q0W8","type":"genericNode","position":{"x":172.18064481781585,"y":67.26574510687044},"data":{"type":"PromptTemplate","node":{"template":{"output_parser":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"output_parser","advanced":false,"dynamic":false,"info":"","type":"BaseOutputParser","list":false},"input_variables":{"required":true,"placeholder":"","show":false,"multiline":false,"password":false,"name":"input_variables","advanced":false,"dynamic":false,"info":"","type":"str","list":true,"value":["history","text"]},"partial_variables":{"required":false,"placeholder":"","show":false,"multiline":false,"password":false,"name":"partial_variables","advanced":false,"dynamic":false,"info":"","type":"code","list":false},"template":{"required":true,"placeholder":"","show":true,"multiline":true,"password":false,"name":"template","advanced":false,"dynamic":false,"info":"","type":"prompt","list":false,"value":"The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\n{history}\nHuman: {text}\nAI:"},"template_format":{"required":false,"placeholder":"","show":false,"multiline":false,"value":"f-string","password":false,"name":"template_format","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"validate_template":{"required":false,"placeholder":"","show":false,"multiline":false,"value":true,"password":false,"name":"validate_template","advanced":false,"dynamic":false,"info":"","type":"bool","list":false},"_type":"PromptTemplate","history":{"required":false,"placeholder":"","show":true,"multiline":true,"value":"","password":false,"name":"history","display_name":"history","advanced":false,"input_types":["Document","BaseOutputParser"],"dynamic":false,"info":"","type":"str","list":false},"text":{"required":false,"placeholder":"","show":true,"multiline":true,"value":"","password":false,"name":"text","display_name":"text","advanced":false,"input_types":["Document","BaseOutputParser"],"dynamic":false,"info":"","type":"str","list":false}},"description":"A prompt template for a language model.","base_classes":["StringPromptTemplate","PromptTemplate","BasePromptTemplate"],"name":"","display_name":"PromptTemplate","documentation":"https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/","custom_fields":{"template":["history","text"]},"output_types":[],"field_formatters":{"formatters":{"openai_api_key":{}},"base_formatters":{"kwargs":{},"optional":{},"list":{},"dict":{},"union":{},"multiline":{},"show":{},"password":{},"default":{},"headers":{},"dict_code_file":{},"model_fields":{"MODEL_DICT":{"OpenAI":["text-davinci-003","text-davinci-002","text-curie-001","text-babbage-001","text-ada-001"],"ChatOpenAI":["gpt-3.5-turbo-0613","gpt-3.5-turbo","gpt-3.5-turbo-16k-0613","gpt-3.5-turbo-16k","gpt-4-0613","gpt-4-32k-0613","gpt-4","gpt-4-32k"],"Anthropic":["claude-v1","claude-v1-100k","claude-instant-v1","claude-instant-v1-100k","claude-v1.3","claude-v1.3-100k","claude-v1.2","claude-v1.0","claude-instant-v1.1","claude-instant-v1.1-100k","claude-instant-v1.0"],"ChatAnthropic":["claude-v1","claude-v1-100k","claude-instant-v1","claude-instant-v1-100k","claude-v1.3","claude-v1.3-100k","claude-v1.2","claude-v1.0","claude-instant-v1.1","claude-instant-v1.1-100k","claude-instant-v1.0"]}}}},"beta":false,"error":null},"id":"PromptTemplate-5Q0W8","value":null},"selected":false,"dragging":false,"positionAbsolute":{"x":172.18064481781585,"y":67.26574510687044}},{"width":384,"height":561,"id":"ConversationBufferMemory-Lu2Nb","type":"genericNode","position":{"x":802.1806448178158,"y":43.265745106870426},"data":{"type":"ConversationBufferMemory","node":{"template":{"chat_memory":{"required":false,"placeholder":"","show":true,"multiline":false,"password":false,"name":"chat_memory","advanced":false,"dynamic":false,"info":"","type":"BaseChatMessageHistory","list":false},"ai_prefix":{"required":false,"placeholder":"","show":false,"multiline":false,"value":"AI","password":false,"name":"ai_prefix","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"human_prefix":{"required":false,"placeholder":"","show":false,"multiline":false,"value":"Human","password":false,"name":"human_prefix","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"input_key":{"required":false,"placeholder":"","show":true,"multiline":false,"value":"","password":false,"name":"input_key","advanced":false,"dynamic":false,"info":"The variable to be used as Chat Input when more than one variable is available.","type":"str","list":false},"memory_key":{"required":false,"placeholder":"","show":true,"multiline":false,"value":"history","password":false,"name":"memory_key","advanced":false,"dynamic":false,"info":"","type":"str","list":false},"output_key":{"required":false,"placeholder":"","show":true,"multiline":false,"value":"","password":false,"name":"output_key","advanced":false,"dynamic":false,"info":"The variable to be used as Chat Output (e.g. answer in a ConversationalRetrievalChain)","type":"str","list":false},"return_messages":{"required":false,"placeholder":"","show":true,"multiline":false,"password":false,"name":"return_messages","advanced":false,"dynamic":false,"info":"","type":"bool","list":false},"_type":"ConversationBufferMemory"},"description":"Buffer for storing conversation memory.","base_classes":["BaseMemory","ConversationBufferMemory","BaseChatMemory"],"display_name":"ConversationBufferMemory","documentation":"https://python.langchain.com/docs/modules/memory/how_to/buffer"},"id":"ConversationBufferMemory-Lu2Nb","value":null},"selected":false,"positionAbsolute":{"x":802.1806448178158,"y":43.265745106870426},"dragging":false}],"edges":[{"source":"ChatOpenAI-vy7fV","sourceHandle":"ChatOpenAI|ChatOpenAI-vy7fV|ChatOpenAI|BaseChatModel|BaseLanguageModel|BaseLLM","target":"LLMChain-UjBh1","targetHandle":"BaseLanguageModel|llm|LLMChain-UjBh1","className":"","id":"reactflow__edge-ChatOpenAI-vy7fVChatOpenAI|ChatOpenAI-vy7fV|ChatOpenAI|BaseChatModel|BaseLanguageModel|BaseLLM-LLMChain-UjBh1BaseLanguageModel|llm|LLMChain-UjBh1","selected":false,"animated":false,"style":{"stroke":"#555"}},{"source":"PromptTemplate-5Q0W8","sourceHandle":"PromptTemplate|PromptTemplate-5Q0W8|StringPromptTemplate|PromptTemplate|BasePromptTemplate","target":"LLMChain-UjBh1","targetHandle":"BasePromptTemplate|prompt|LLMChain-UjBh1","className":"","id":"reactflow__edge-PromptTemplate-5Q0W8PromptTemplate|PromptTemplate-5Q0W8|StringPromptTemplate|PromptTemplate|BasePromptTemplate-LLMChain-UjBh1BasePromptTemplate|prompt|LLMChain-UjBh1","animated":false,"style":{"stroke":"#555"}},{"source":"ConversationBufferMemory-Lu2Nb","sourceHandle":"ConversationBufferMemory|ConversationBufferMemory-Lu2Nb|BaseMemory|ConversationBufferMemory|BaseChatMemory","target":"LLMChain-UjBh1","targetHandle":"BaseMemory|memory|LLMChain-UjBh1","className":"","id":"reactflow__edge-ConversationBufferMemory-Lu2NbConversationBufferMemory|ConversationBufferMemory-Lu2Nb|BaseMemory|ConversationBufferMemory|BaseChatMemory-LLMChain-UjBh1BaseMemory|memory|LLMChain-UjBh1","animated":false,"style":{"stroke":"#555"}}],"viewport":{"x":-64.70809474436828,"y":44.7801470275611,"zoom":0.6622606580990782}},"id":"0cdfb2f2-19de-4e15-99fa-fd5203b38053"} \ No newline at end of file diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 376de6f4e..29c5d5a91 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -360,3 +360,92 @@ def test_various_prompts(client, prompt, expected_input_variables): response = client.post("api/v1/validate/prompt", json=PROMPT_REQUEST) assert response.status_code == 200 assert response.json()["input_variables"] == expected_input_variables + + +def test_basic_chat_in_process(client, added_flow, created_api_key): + # Run the /api/v1/process/{flow_id} endpoint + headers = {"x-api-key": created_api_key.api_key} + post_data = {"inputs": {"text": "Hi, My name is Gabriel"}} + response = client.post( + f"api/v1/process/{added_flow.get('id')}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + # Check the response + assert "Gabriel" in response.json()["result"]["text"] + # session_id should be returned + assert "session_id" in response.json() + assert response.json()["session_id"] is not None + # New request with the same session_id + # asking "What is my name?" should return "Gabriel" + post_data = { + "inputs": {"text": "What is my name?"}, + "session_id": response.json()["session_id"], + } + response = client.post( + f"api/v1/process/{added_flow.get('id')}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + assert "Gabriel" in response.json()["result"]["text"] + + +def test_basic_chat_different_session_ids(client, added_flow, created_api_key): + # Run the /api/v1/process/{flow_id} endpoint + headers = {"x-api-key": created_api_key.api_key} + post_data = {"inputs": {"text": "Hi, My name is Gabriel"}} + response = client.post( + f"api/v1/process/{added_flow.get('id')}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + # Check the response + assert "Gabriel" in response.json()["result"]["text"] + # session_id should be returned + assert "session_id" in response.json() + assert response.json()["session_id"] is not None + # New request with a different session_id + # asking "What is my name?" should return "Gabriel" + post_data = { + "inputs": {"text": "What is my name?"}, + "session_id": "other session id", + } + response = client.post( + f"api/v1/process/{added_flow.get('id')}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + assert "Gabriel" not in response.json()["result"]["text"] + + +@pytest.mark.parametrize("name", ["Gabriel", "John"]) +def test_basic_chat_with_two_session_ids_and_names( + client, added_flow, created_api_key, name +): + headers = {"x-api-key": created_api_key.api_key} + + def run_api_post(endpoint, headers, post_data): + response = client.post(endpoint, headers=headers, json=post_data) + assert response.status_code == 200, response.json() + return response.json() + + def assert_name_and_session_in_response(name, response_json): + assert name in response_json["result"]["text"] + assert "session_id" in response_json + assert response_json["session_id"] is not None + + # Run the /api/v1/process/{flow_id} endpoint + flow_id = added_flow.get("id") + post_data = {"inputs": {"text": f"Hi, My name is {name}"}} + response_json = run_api_post(f"api/v1/process/{flow_id}", headers, post_data) + assert_name_and_session_in_response(name, response_json) + session_id = response_json["session_id"] + + # New request with the same session_id asking "What is my name?" should return name + post_data = {"inputs": {"text": "What is my name?"}, "session_id": session_id} + response_json = run_api_post(f"api/v1/process/{flow_id}", headers, post_data) + assert name in response_json["result"]["text"] From 8c8e26472db0e17efa49b06e0553fe7869e285ae Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 12:23:33 -0300 Subject: [PATCH 097/213] =?UTF-8?q?=F0=9F=90=9B=20fix(process.py):=20add?= =?UTF-8?q?=20missing=20else=20statement=20to=20properly=20assign=20sessio?= =?UTF-8?q?n=5Fid=20when=20not=20generating=20a=20new=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 8fe825e3d..8e0e9771e 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -192,6 +192,8 @@ async def process_graph_cached( session_id = session_manager.generate_key( session_id=session_id, data_graph=data_graph ) + else: + session_id = session_manager.build_key(session_id, data_graph) # Load the graph using SessionManager langchain_object, artifacts = session_manager.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) From 5b6c732213972a9e69c1914c4e84f367cee013a5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 12:24:07 -0300 Subject: [PATCH 098/213] =?UTF-8?q?=F0=9F=94=A7=20chore(manager.py):=20ref?= =?UTF-8?q?actor=20load=5Fsession=20method=20to=20use=20a=20more=20descrip?= =?UTF-8?q?tive=20variable=20name=20'key'=20instead=20of=20'session=5Fid'?= =?UTF-8?q?=20for=20better=20readability=20=F0=9F=94=A7=20chore(manager.py?= =?UTF-8?q?):=20refactor=20generate=5Fkey=20method=20to=20use=20a=20separa?= =?UTF-8?q?te=20build=5Fkey=20method=20for=20better=20separation=20of=20co?= =?UTF-8?q?ncerns=20=F0=9F=94=A7=20chore(manager.py):=20refactor=20update?= =?UTF-8?q?=5Fsession=20method=20to=20use=20the=20build=5Fkey=20method=20f?= =?UTF-8?q?or=20better=20separation=20of=20concerns=20=F0=9F=94=A7=20chore?= =?UTF-8?q?(manager.py):=20refactor=20clear=5Fsession=20method=20to=20use?= =?UTF-8?q?=20the=20build=5Fkey=20method=20for=20better=20separation=20of?= =?UTF-8?q?=20concerns=20=F0=9F=94=A7=20chore(manager.py):=20add=20type=20?= =?UTF-8?q?hinting=20for=20cache=5Fmanager=20parameter=20in=20the=20=5F=5F?= =?UTF-8?q?init=5F=5F=20method=20for=20better=20code=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/session/manager.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index 76ac42113..61cc31b39 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -1,19 +1,21 @@ +from typing import TYPE_CHECKING from langflow.interface.run import build_sorted_vertices from langflow.services.base import Service from langflow.services.cache.utils import compute_dict_hash from langflow.services.session.utils import session_id_generator +if TYPE_CHECKING: + from langflow.services.cache.base import BaseCacheManager + class SessionManager(Service): name = "session_manager" def __init__(self, cache_manager): - self.cache_manager = cache_manager - - def load_session(self, session_id, data_graph): - key = self.generate_key(session_id, data_graph) + self.cache_manager: "BaseCacheManager" = cache_manager + def load_session(self, key, data_graph): # Check if the data is cached if key in self.cache_manager: return self.cache_manager.get(key) @@ -24,18 +26,21 @@ class SessionManager(Service): return graph, artifacts + def build_key(self, session_id, data_graph): + json_hash = compute_dict_hash(data_graph) + return f"{session_id}{':' if session_id else ''}{json_hash}" + def generate_key(self, session_id, data_graph): # Hash the JSON and combine it with the session_id to create a unique key - json_hash = compute_dict_hash(data_graph) if session_id is None: # generate a 5 char session_id to concatenate with the json_hash session_id = session_id_generator() - return f"{session_id}{':' if session_id else ''}{json_hash}" + return self.build_key(session_id, data_graph=data_graph) def update_session(self, session_id, data_graph, value): - key = self.generate_key(session_id, data_graph) + key = self.build_key(session_id, data_graph) self.cache_manager.set(key, value) def clear_session(self, session_id, data_graph): - key = self.generate_key(session_id, data_graph) + key = self.build_key(session_id, data_graph) self.cache_manager.delete(key) From 141f57958bbc70c97a19440eaedff3554b0a99b9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 12:24:43 -0300 Subject: [PATCH 099/213] =?UTF-8?q?=E2=9C=A8=20feat(tests):=20add=20=5F=5F?= =?UTF-8?q?init=5F=5F.py=20and=20utils.py=20files=20to=20tests=20directory?= =?UTF-8?q?=20for=20test=20setup=20and=20utility=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/__init__.py | 0 tests/utils.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/utils.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 000000000..ce913c4b9 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,8 @@ +def run_post(client, flow_id, headers, post_data): + response = client.post( + f"api/v1/process/{flow_id}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + return response.json() From 59033785035bd2f11351aa44d7dd7a4d26f6d3ea Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 14:46:55 -0300 Subject: [PATCH 100/213] =?UTF-8?q?=F0=9F=90=9B=20fix(process.py):=20remov?= =?UTF-8?q?e=20unnecessary=20else=20statement=20to=20improve=20code=20read?= =?UTF-8?q?ability=20and=20semantics=20=F0=9F=90=9B=20fix(process.py):=20u?= =?UTF-8?q?pdate=20session=5Fmanager.update=5Fsession()=20method=20signatu?= =?UTF-8?q?re=20to=20match=20the=20new=20implementation=20=F0=9F=90=9B=20f?= =?UTF-8?q?ix(worker.py):=20update=20session=5Fmanager.update=5Fsession()?= =?UTF-8?q?=20method=20signature=20to=20match=20the=20new=20implementation?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(test=5Fprocess.py):=20update=20session=5F?= =?UTF-8?q?manager.build=5Fkey()=20and=20session=5Fmanager.clear=5Fsession?= =?UTF-8?q?()=20method=20calls=20to=20match=20the=20new=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 6 +----- src/backend/langflow/worker.py | 4 +--- tests/test_process.py | 7 ++++--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 8e0e9771e..10bdbd876 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -192,17 +192,13 @@ async def process_graph_cached( session_id = session_manager.generate_key( session_id=session_id, data_graph=data_graph ) - else: - session_id = session_manager.build_key(session_id, data_graph) # Load the graph using SessionManager langchain_object, artifacts = session_manager.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) result = generate_result(langchain_object, processed_inputs) # langchain_object is now updated with the new memory # we need to update the cache with the updated langchain_object - session_manager.update_session( - session_id, data_graph, (langchain_object, artifacts) - ) + session_manager.update_session(session_id, (langchain_object, artifacts)) return Result(result, session_id) diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 4790090f0..2b66b160c 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -50,8 +50,6 @@ def process_graph_cached_task( result = generate_result(langchain_object, processed_inputs) # langchain_object is now updated with the new memory # we need to update the cache with the updated langchain_object - session_manager.update_session( - session_id, data_graph, (langchain_object, artifacts) - ) + session_manager.update_session(session_id, (langchain_object, artifacts)) return result, session_id diff --git a/tests/test_process.py b/tests/test_process.py index 6d396f974..0628676ee 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -217,14 +217,15 @@ def test_load_langchain_object_with_no_cached_session(client, basic_graph_data): # Provide a non-existent session_id session_manager = get_session_manager() session_id1 = "non-existent-session-id" + session_id = session_manager.build_key(session_id1, basic_graph_data) langchain_object1, artifacts1 = session_manager.load_session( - session_id1, basic_graph_data + session_id, basic_graph_data ) # Clear the cache - session_manager.clear_session(session_id1, basic_graph_data) + session_manager.clear_session(session_id) # Use the new session_id to get the langchain_object again langchain_object2, artifacts2 = session_manager.load_session( - session_id1, basic_graph_data + session_id, basic_graph_data ) assert id(langchain_object1) != id( From 9615523a8dfc9f911c39a3f6b1ee9be84e918e56 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 14:49:10 -0300 Subject: [PATCH 101/213] =?UTF-8?q?=F0=9F=94=A7=20refactor(manager.py):=20?= =?UTF-8?q?simplify=20update=5Fsession=20and=20clear=5Fsession=20methods?= =?UTF-8?q?=20by=20removing=20unnecessary=20data=5Fgraph=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/session/manager.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index 61cc31b39..de9fa2c5d 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -37,10 +37,8 @@ class SessionManager(Service): session_id = session_id_generator() return self.build_key(session_id, data_graph=data_graph) - def update_session(self, session_id, data_graph, value): - key = self.build_key(session_id, data_graph) - self.cache_manager.set(key, value) + def update_session(self, session_id, value): + self.cache_manager.set(session_id, value) - def clear_session(self, session_id, data_graph): - key = self.build_key(session_id, data_graph) - self.cache_manager.delete(key) + def clear_session(self, session_id): + self.cache_manager.delete(session_id) From 00290d0f9ef5317d10d0817f81920fb5bd9fce85 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 14:49:20 -0300 Subject: [PATCH 102/213] =?UTF-8?q?=F0=9F=94=A7=20fix(test=5Fendpoints.py)?= =?UTF-8?q?:=20remove=20unused=20imports=20and=20refactor=20test=5Fbasic?= =?UTF-8?q?=5Fchat=5Fwith=5Ftwo=5Fsession=5Fids=5Fand=5Fnames=20to=20use?= =?UTF-8?q?=20a=20loop=20for=20better=20readability=20and=20maintainabilit?= =?UTF-8?q?y=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20test=5Fbasic?= =?UTF-8?q?=5Fchat=5Fwith=5Ftwo=5Fsession=5Fids=5Fand=5Fnames=20to=20test?= =?UTF-8?q?=20the=20functionality=20of=20handling=20multiple=20session=20I?= =?UTF-8?q?Ds=20and=20names=20in=20the=20chat=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_endpoints.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 29c5d5a91..fac7adb34 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -8,6 +8,8 @@ from fastapi.testclient import TestClient from langflow.interface.tools.constants import CUSTOM_TOOLS from langflow.template.frontend_node.chains import TimeTravelGuideChainNode +from tests.utils import run_post + PROMPT_REQUEST = { "name": "string", @@ -422,30 +424,27 @@ def test_basic_chat_different_session_ids(client, added_flow, created_api_key): assert "Gabriel" not in response.json()["result"]["text"] -@pytest.mark.parametrize("name", ["Gabriel", "John"]) -def test_basic_chat_with_two_session_ids_and_names( - client, added_flow, created_api_key, name -): +def test_basic_chat_with_two_session_ids_and_names(client, added_flow, created_api_key): headers = {"x-api-key": created_api_key.api_key} + flow_id = added_flow.get("id") + names = ["Gabriel", "John"] + session_ids = [] - def run_api_post(endpoint, headers, post_data): - response = client.post(endpoint, headers=headers, json=post_data) - assert response.status_code == 200, response.json() - return response.json() + for name in names: + post_data = {"inputs": {"text": f"Hi, My name is {name}"}} + response_json = run_post(client, flow_id, headers, post_data) - def assert_name_and_session_in_response(name, response_json): assert name in response_json["result"]["text"] assert "session_id" in response_json assert response_json["session_id"] is not None - # Run the /api/v1/process/{flow_id} endpoint - flow_id = added_flow.get("id") - post_data = {"inputs": {"text": f"Hi, My name is {name}"}} - response_json = run_api_post(f"api/v1/process/{flow_id}", headers, post_data) - assert_name_and_session_in_response(name, response_json) - session_id = response_json["session_id"] + session_ids.append(response_json["session_id"]) - # New request with the same session_id asking "What is my name?" should return name - post_data = {"inputs": {"text": "What is my name?"}, "session_id": session_id} - response_json = run_api_post(f"api/v1/process/{flow_id}", headers, post_data) - assert name in response_json["result"]["text"] + for i, name in enumerate(names): + post_data = { + "inputs": {"text": "What is my name?"}, + "session_id": session_ids[i], + } + response_json = run_post(client, flow_id, headers, post_data) + + assert name in response_json["result"]["text"] From 7a63b5635d8977e3259f14a634344f70d5923fb6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 14:53:38 -0300 Subject: [PATCH 103/213] =?UTF-8?q?=F0=9F=94=A5=20refactor(run.py):=20remo?= =?UTF-8?q?ve=20unused=20imports=20and=20commented=20out=20code=20in=20run?= =?UTF-8?q?.py=20=F0=9F=94=A5=20refactor(test=5Fcache.py):=20remove=20unus?= =?UTF-8?q?ed=20imports=20and=20commented=20out=20code=20in=20test=5Fcache?= =?UTF-8?q?.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/run.py | 31 --------------------------- tests/test_cache.py | 12 ----------- 2 files changed, 43 deletions(-) diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 83d8ab305..81c3f6910 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -1,22 +1,8 @@ from typing import Any, Dict, Tuple -from langflow.services.cache.utils import Memoize from langflow.graph import Graph -from langflow.services.utils import get_cache_manager from loguru import logger -@Memoize(get_cache_manager=get_cache_manager) -def build_langchain_object_with_caching(data_graph): - """ - Build langchain object from data_graph. - """ - - logger.debug("Building langchain object") - graph = Graph.from_payload(data_graph) - return graph.build() - - -# @Memoize(get_cache_manager=get_cache_manager) def build_sorted_vertices(data_graph) -> Tuple[Any, Dict]: """ Build langchain object from data_graph. @@ -32,23 +18,6 @@ def build_sorted_vertices(data_graph) -> Tuple[Any, Dict]: artifacts.update(vertex.artifacts) return graph.build(), artifacts - # def build_sorted_vertices_with_caching(data_graph): - # # Build the result if not in cache - # logger.debug("Building langchain object") - # graph = Graph.from_payload(data_graph) - # sorted_vertices = graph.topological_sort() - # artifacts = {} - # for vertex in sorted_vertices: - # vertex.build() - # if vertex.artifacts: - # artifacts.update(vertex.artifacts) - # result = (graph.build(), artifacts) - - # Save to cache - # cache_manager.set(session_id, result) - - # return result - def build_langchain_object(data_graph): """ diff --git a/tests/test_cache.py b/tests/test_cache.py index 94e4e8553..4ceea2c2a 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -1,11 +1,7 @@ import json from langflow.graph import Graph -from langflow.services.cache.utils import compute_dict_hash import pytest -from langflow.interface.run import ( - build_langchain_object_with_caching, -) def get_graph(_type="basic"): @@ -41,14 +37,6 @@ def langchain_objects_are_equal(obj1, obj2): return str(obj1) == str(obj2) -# Test build_langchain_object_with_caching -def test_build_langchain_object_with_caching(client, basic_data_graph): - session_id = compute_dict_hash(basic_data_graph) - build_langchain_object_with_caching.clear_cache(session_id) - graph = build_langchain_object_with_caching(basic_data_graph) - assert graph is not None - - # Test build_graph def test_build_graph(basic_data_graph): graph = Graph.from_payload(basic_data_graph) From 18af7162907b0ef65b2e0d2fb27e84524d5f850b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 14:54:03 -0300 Subject: [PATCH 104/213] =?UTF-8?q?=F0=9F=90=9B=20fix(chat.py):=20update?= =?UTF-8?q?=20variable=20name=20from=20in=5Fmemory=5Fcache=20to=20cache=5F?= =?UTF-8?q?manager=20for=20better=20clarity=20and=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index 1cccc654c..4e7807ab8 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -45,7 +45,7 @@ async def chat( code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized" ) - if client_id in chat_manager.in_memory_cache: + if client_id in chat_manager.cache_manager: await chat_manager.handle_websocket(client_id, websocket) else: # We accept the connection but close it immediately From e50704a49f60b4d20efcc69aa90b24488f907f92 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:32:54 -0300 Subject: [PATCH 105/213] =?UTF-8?q?=F0=9F=90=9B=20fix(celery.py):=20fix=20?= =?UTF-8?q?type=20hinting=20error=20for=20AsyncResult=20import=20?= =?UTF-8?q?=F0=9F=94=92=20chore(celery.py):=20add=20check=20for=20delay=20?= =?UTF-8?q?method=20existence=20in=20launch=5Ftask=20to=20prevent=20errors?= =?UTF-8?q?=20=F0=9F=94=92=20chore(celery.py):=20change=20get=5Ftask=20ret?= =?UTF-8?q?urn=20type=20hint=20to=20Any=20to=20match=20actual=20return=20t?= =?UTF-8?q?ype?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/task/backends/celery.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/services/task/backends/celery.py b/src/backend/langflow/services/task/backends/celery.py index 04e26159c..3a6e1f450 100644 --- a/src/backend/langflow/services/task/backends/celery.py +++ b/src/backend/langflow/services/task/backends/celery.py @@ -1,5 +1,5 @@ from typing import Any, Callable -from celery.result import AsyncResult +from celery.result import AsyncResult # type: ignore from langflow.services.task.backends.base import TaskBackend from langflow.worker import celery_app @@ -11,8 +11,10 @@ class CeleryBackend(TaskBackend): def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any ) -> str: - task = task_func.apply_async(args=args, kwargs=kwargs) + if not hasattr(task_func, "delay"): + raise ValueError(f"Task function {task_func} does not have a delay method") + task = task_func.delay(*args, **kwargs) return task.id - def get_task(self, task_id: str) -> AsyncResult: + def get_task(self, task_id: str) -> Any: return AsyncResult(task_id, app=self.celery_app) From 8a47fb75cba8089857e6e969f364297a70ab8580 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:33:11 -0300 Subject: [PATCH 106/213] =?UTF-8?q?=F0=9F=94=A8=20refactor(base.py):=20rem?= =?UTF-8?q?ove=20unnecessary=20type=20hinting=20for=20task=5Fid=20paramete?= =?UTF-8?q?r=20in=20get=5Ftask=20method=20=F0=9F=94=A8=20refactor(base.py)?= =?UTF-8?q?:=20remove=20unnecessary=20return=20type=20hinting=20for=20laun?= =?UTF-8?q?ch=5Ftask=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/task/backends/base.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/services/task/backends/base.py b/src/backend/langflow/services/task/backends/base.py index a05c760e7..ccbd9273b 100644 --- a/src/backend/langflow/services/task/backends/base.py +++ b/src/backend/langflow/services/task/backends/base.py @@ -1,14 +1,12 @@ from abc import ABC, abstractmethod -from typing import Any, Callable, Union +from typing import Any, Callable class TaskBackend(ABC): @abstractmethod - def launch_task( - self, task_func: Callable[..., Any], *args: Any, **kwargs: Any - ) -> Union[int, str]: + def launch_task(self, task_func: Callable[..., Any], *args: Any, **kwargs: Any): pass @abstractmethod - def get_task(self, task_id: Union[int, str]) -> Any: + def get_task(self, task_id: str) -> Any: pass From 779c021d78983dc7d6a2bfe3babf1a1f01fbe75b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:33:31 -0300 Subject: [PATCH 107/213] =?UTF-8?q?=F0=9F=90=9B=20fix(anyio.py):=20change?= =?UTF-8?q?=20return=20type=20annotation=20of=20result()=20method=20from?= =?UTF-8?q?=20'any'=20to=20'Any'=20for=20better=20type=20hinting=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(anyio.py):=20change=20parameter=20type=20ann?= =?UTF-8?q?otation=20of=20get=5Ftask()=20method=20from=20'int'=20to=20'str?= =?UTF-8?q?'=20to=20match=20the=20actual=20type=20of=20task=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/task/backends/anyio.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/services/task/backends/anyio.py b/src/backend/langflow/services/task/backends/anyio.py index 68c449d4e..20691212d 100644 --- a/src/backend/langflow/services/task/backends/anyio.py +++ b/src/backend/langflow/services/task/backends/anyio.py @@ -17,7 +17,7 @@ class AnyIOTaskResult: return self._status @property - def result(self) -> any: + def result(self) -> Any: return self._result def ready(self) -> bool: @@ -41,10 +41,10 @@ class AnyIOBackend(TaskBackend): ) -> Tuple[str, AnyIOTaskResult]: # sourcery skip: remove-unnecessary-cast async with anyio.create_task_group() as tg: task_result = AnyIOTaskResult(tg) - await tg.spawn(task_result.run, task_func, *args, **kwargs) + tg.start_soon(task_result.run, task_func, *args, **kwargs) task_id = str(id(task_result)) self.tasks[task_id] = task_result return task_id, task_result - def get_task(self, task_id: int) -> AnyIOTaskResult: + def get_task(self, task_id: str) -> Any: return self.tasks.get(task_id) From 9721e9d6cf38ae3d8e8776bbcbbbd6aa68ed77de Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:33:43 -0300 Subject: [PATCH 108/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20add?= =?UTF-8?q?=20validation=20to=20check=20if=20task=20function=20has=20an=20?= =?UTF-8?q?apply=20method=20before=20calling=20it=20=E2=9C=A8=20feat(manag?= =?UTF-8?q?er.py):=20change=20return=20type=20of=20launch=5Ftask=20method?= =?UTF-8?q?=20to=20Any=20to=20allow=20for=20more=20flexibility=20in=20retu?= =?UTF-8?q?rn=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/task/manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py index c3e5e6444..cdf731917 100644 --- a/src/backend/langflow/services/task/manager.py +++ b/src/backend/langflow/services/task/manager.py @@ -42,13 +42,15 @@ class TaskManager(Service): ) -> Any: if not self.use_celery: return None, await task_func(*args, **kwargs) + if not hasattr(task_func, "apply"): + raise ValueError(f"Task function {task_func} does not have an apply method") task = task_func.apply(args=args, kwargs=kwargs) result = task.get() return task.id, result async def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any - ) -> Union[str, str]: + ) -> Any: return await self.backend.launch_task(task_func, *args, **kwargs) def get_task(self, task_id: Union[int, str]) -> Any: From a6322461c047ad0f1bbeff07aace9554d5346de7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:34:08 -0300 Subject: [PATCH 109/213] =?UTF-8?q?=F0=9F=90=9B=20fix(api=5Fkey/crud.py):?= =?UTF-8?q?=20fix=20variable=20name=20conflict=20in=20update=5Ftotal=5Fuse?= =?UTF-8?q?s=20function=20to=20prevent=20overwriting=20the=20api=5Fkey=20p?= =?UTF-8?q?arameter=20=F0=9F=94=92=20chore(api=5Fkey/crud.py):=20add=20err?= =?UTF-8?q?or=20handling=20to=20update=5Ftotal=5Fuses=20function=20to=20ra?= =?UTF-8?q?ise=20ValueError=20if=20API=20Key=20is=20not=20found?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/database/models/api_key/crud.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/services/database/models/api_key/crud.py b/src/backend/langflow/services/database/models/api_key/crud.py index 756236018..c1c6f786e 100644 --- a/src/backend/langflow/services/database/models/api_key/crud.py +++ b/src/backend/langflow/services/database/models/api_key/crud.py @@ -67,9 +67,11 @@ def update_total_uses(session, api_key: ApiKey): # but session is not thread safe so we need to create a new session with Session(session.get_bind()) as new_session: - api_key = new_session.get(ApiKey, api_key.id) - api_key.total_uses += 1 - api_key.last_used_at = datetime.datetime.now(datetime.timezone.utc) - new_session.add(api_key) + new_api_key = new_session.get(ApiKey, api_key.id) + if new_api_key is None: + raise ValueError("API Key not found") + new_api_key.total_uses += 1 + new_api_key.last_used_at = datetime.datetime.now(datetime.timezone.utc) + new_session.add(new_api_key) new_session.commit() - return api_key + return new_api_key From e74fc00442481f807e448c99a9630b814dd3c5f3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:34:20 -0300 Subject: [PATCH 110/213] =?UTF-8?q?=F0=9F=94=A7=20chore(base.py):=20add=20?= =?UTF-8?q?Redis=20configuration=20options=20to=20base=20settings=20for=20?= =?UTF-8?q?better=20flexibility=20and=20customization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/settings/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py index 44c1c5468..951859f00 100644 --- a/src/backend/langflow/services/settings/base.py +++ b/src/backend/langflow/services/settings/base.py @@ -42,6 +42,12 @@ class Settings(BaseSettings): COMPONENTS_PATH: List[str] = [] LANGCHAIN_CACHE: str = "InMemoryCache" + # Redis + REDIS_HOST: str = "localhost" + REDIS_PORT: int = 6379 + REDIS_DB: int = 0 + REDIS_CACHE_EXPIRE: int = 3600 + @validator("CONFIG_DIR", pre=True, allow_reuse=True) def set_langflow_dir(cls, value): if not value: From ba9d57fa7853f942dacb99e6b8fb84012f0fe6d8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:34:35 -0300 Subject: [PATCH 111/213] =?UTF-8?q?=F0=9F=94=A5=20refactor(chat/manager.py?= =?UTF-8?q?):=20remove=20unused=20imports=20and=20code=20=F0=9F=9A=80=20fe?= =?UTF-8?q?at(chat/manager.py):=20add=20import=20for=20pil=5Fto=5Fbase64?= =?UTF-8?q?=20function=20to=20support=20image=20encoding=20in=20chat=20mes?= =?UTF-8?q?sages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/chat/manager.py | 222 ------------------ src/backend/langflow/services/chat/manager.py | 1 + 2 files changed, 1 insertion(+), 222 deletions(-) delete mode 100644 src/backend/langflow/chat/manager.py diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py deleted file mode 100644 index 17968e32a..000000000 --- a/src/backend/langflow/chat/manager.py +++ /dev/null @@ -1,222 +0,0 @@ -from collections import defaultdict -from fastapi import WebSocket, status -from langflow.api.v1.schemas import ChatMessage, ChatResponse, FileResponse -from langflow.cache import cache_manager -from langflow.cache.manager import Subject -from langflow.chat.utils import process_graph -from langflow.interface.utils import pil_to_base64 -from langflow.utils.logger import logger - - -import asyncio -import json -from typing import Any, Dict, List - -from langflow.cache.flow import InMemoryCache - - -class ChatHistory(Subject): - def __init__(self): - super().__init__() - self.history: Dict[str, List[ChatMessage]] = defaultdict(list) - - def add_message(self, client_id: str, message: ChatMessage): - """Add a message to the chat history.""" - - self.history[client_id].append(message) - - if not isinstance(message, FileResponse): - self.notify() - - def get_history(self, client_id: str, filter_messages=True) -> List[ChatMessage]: - """Get the chat history for a client.""" - if history := self.history.get(client_id, []): - if filter_messages: - return [msg for msg in history if msg.type not in ["start", "stream"]] - return history - else: - return [] - - def empty_history(self, client_id: str): - """Empty the chat history for a client.""" - self.history[client_id] = [] - - -class ChatManager: - def __init__(self): - self.active_connections: Dict[str, WebSocket] = {} - self.chat_history = ChatHistory() - self.cache_manager = cache_manager - self.cache_manager.attach(self.update) - self.in_memory_cache = InMemoryCache() - - def on_chat_history_update(self): - """Send the last chat message to the client.""" - client_id = self.cache_manager.current_client_id - if client_id in self.active_connections: - chat_response = self.chat_history.get_history( - client_id, filter_messages=False - )[-1] - if chat_response.is_bot: - # Process FileResponse - if isinstance(chat_response, FileResponse): - # If data_type is pandas, convert to csv - if chat_response.data_type == "pandas": - chat_response.data = chat_response.data.to_csv() - elif chat_response.data_type == "image": - # Base64 encode the image - chat_response.data = pil_to_base64(chat_response.data) - # get event loop - loop = asyncio.get_event_loop() - - coroutine = self.send_json(client_id, chat_response) - asyncio.run_coroutine_threadsafe(coroutine, loop) - - def update(self): - if self.cache_manager.current_client_id in self.active_connections: - self.last_cached_object_dict = self.cache_manager.get_last() - # Add a new ChatResponse with the data - chat_response = FileResponse( - message=None, - type="file", - data=self.last_cached_object_dict["obj"], - data_type=self.last_cached_object_dict["type"], - ) - - self.chat_history.add_message( - self.cache_manager.current_client_id, chat_response - ) - - async def connect(self, client_id: str, websocket: WebSocket): - await websocket.accept() - self.active_connections[client_id] = websocket - - def disconnect(self, client_id: str): - self.active_connections.pop(client_id, None) - - async def send_message(self, client_id: str, message: str): - websocket = self.active_connections[client_id] - await websocket.send_text(message) - - async def send_json(self, client_id: str, message: ChatMessage): - websocket = self.active_connections[client_id] - await websocket.send_json(message.dict()) - - async def close_connection(self, client_id: str, code: int, reason: str): - if websocket := self.active_connections[client_id]: - try: - await websocket.close(code=code, reason=reason) - self.disconnect(client_id) - except RuntimeError as exc: - # This is to catch the following error: - # Unexpected ASGI message 'websocket.close', after sending 'websocket.close' - if "after sending" in str(exc): - logger.error(f"Error closing connection: {exc}") - - async def process_message( - self, client_id: str, payload: Dict, langchain_object: Any - ): - # Process the graph data and chat message - chat_inputs = payload.pop("inputs", {}) - chat_inputs = ChatMessage(message=chat_inputs) - self.chat_history.add_message(client_id, chat_inputs) - - # graph_data = payload - start_resp = ChatResponse(message=None, type="start", intermediate_steps="") - await self.send_json(client_id, start_resp) - - # is_first_message = len(self.chat_history.get_history(client_id=client_id)) <= 1 - # Generate result and thought - try: - logger.debug("Generating result and thought") - - result, intermediate_steps = await process_graph( - langchain_object=langchain_object, - chat_inputs=chat_inputs, - websocket=self.active_connections[client_id], - ) - self.cache_manager.set(client_id, langchain_object) - except Exception as e: - # Log stack trace - logger.exception(e) - self.chat_history.empty_history(client_id) - raise e - # Send a response back to the frontend, if needed - intermediate_steps = intermediate_steps or "" - history = self.chat_history.get_history(client_id, filter_messages=False) - file_responses = [] - if history: - # Iterate backwards through the history - for msg in reversed(history): - if isinstance(msg, FileResponse): - if msg.data_type == "image": - # Base64 encode the image - if isinstance(msg.data, str): - continue - msg.data = pil_to_base64(msg.data) - file_responses.append(msg) - if msg.type == "start": - break - - response = ChatResponse( - message=result, - intermediate_steps=intermediate_steps.strip(), - type="end", - files=file_responses, - ) - await self.send_json(client_id, response) - self.chat_history.add_message(client_id, response) - - def set_cache(self, client_id: str, langchain_object: Any) -> bool: - """ - Set the cache for a client. - """ - result_dict = {"result": langchain_object, "type": type(langchain_object)} - self.cache_manager.upsert(client_id, result_dict) - return client_id in self.cache_manager - - async def handle_websocket(self, client_id: str, websocket: WebSocket): - await self.connect(client_id, websocket) - - try: - chat_history = self.chat_history.get_history(client_id) - # iterate and make BaseModel into dict - chat_history = [chat.dict() for chat in chat_history] - await websocket.send_json(chat_history) - - while True: - json_payload = await websocket.receive_json() - try: - payload = json.loads(json_payload) - except TypeError: - payload = json_payload - if "clear_history" in payload: - self.chat_history.history[client_id] = [] - continue - - with self.cache_manager.set_client_id(client_id): - if langchain_object := self.in_memory_cache.get(client_id).get( - "result" - ): - await self.process_message(client_id, payload, langchain_object) - - else: - raise ValueError("No langchain object found in cache") - except Exception as exc: - # Handle any exceptions that might occur - logger.error(f"Error handling websocket: {exc}") - await self.close_connection( - client_id=client_id, - code=status.WS_1011_INTERNAL_ERROR, - reason=str(exc)[:120], - ) - finally: - try: - await self.close_connection( - client_id=client_id, - code=status.WS_1000_NORMAL_CLOSURE, - reason="Client disconnected", - ) - except Exception as exc: - logger.error(f"Error closing connection: {exc}") - self.disconnect(client_id) diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index d0292fc71..5bd46b32d 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -1,6 +1,7 @@ from collections import defaultdict from fastapi import WebSocket, status from langflow.api.v1.schemas import ChatMessage, ChatResponse, FileResponse +from langflow.interface.utils import pil_to_base64 from langflow.services.base import Service from langflow.services.chat.cache import Subject from langflow.services.chat.utils import process_graph From d13f0e77dfe94ae015d47cae5256f529b3cb9b5a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:34:45 -0300 Subject: [PATCH 112/213] =?UTF-8?q?=F0=9F=94=A7=20chore(utils.py):=20remov?= =?UTF-8?q?e=20unused=20imports=20and=20classes=20=F0=9F=94=A5=20refactor(?= =?UTF-8?q?utils.py):=20remove=20unused=20Memoize=20class=20and=20its=20me?= =?UTF-8?q?thods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/utils.py | 45 +------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/src/backend/langflow/services/cache/utils.py b/src/backend/langflow/services/cache/utils.py index 04fa4bbdf..5791e65c5 100644 --- a/src/backend/langflow/services/cache/utils.py +++ b/src/backend/langflow/services/cache/utils.py @@ -6,12 +6,12 @@ import os import tempfile from collections import OrderedDict from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Dict +from typing import TYPE_CHECKING, Any, Dict from appdirs import user_cache_dir from langflow.services.database.models.base import orjson_dumps if TYPE_CHECKING: - from langflow.services.cache.base import BaseCacheManager + pass CACHE: Dict[str, Any] = {} @@ -195,44 +195,3 @@ def save_uploaded_file(file, folder_name): new_file.write(chunk) return file_path - - -class Memoize: - def __init__( - self, - get_cache_manager: Callable[[], "BaseCacheManager"], - ): - self.get_cache_manager = get_cache_manager - self.hash_func = compute_dict_hash - - def clear_cache(self, session_id): - cache_manager = self.get_cache_manager() - cache_manager.delete(session_id) - - def get_result_by_session_id(self, session_id): - cache_manager = self.get_cache_manager() - return cache_manager.get(session_id) - - def update_cache(self, session_id, value): - cache_manager = self.get_cache_manager() - cache_manager.set(session_id, value) - - def __call__(self, func: Callable[..., Any]): - @functools.wraps(func) - def wrapper(*args, **kwargs): - cache_manager = self.get_cache_manager() - session_id = self.hash_func(args[0]) - - result = cache_manager.get(session_id) - - if result is None: - result = func(*args, **kwargs) - cache_manager.set(session_id, result) - - wrapper.session_id = session_id - return result - - wrapper.clear_cache = self.clear_cache - wrapper.update_cache = self.update_cache - wrapper.get_result_by_session_id = self.get_result_by_session_id - return wrapper From 44bd6e4787f5e85697db7f44b671e7612974c2c5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:34:56 -0300 Subject: [PATCH 113/213] =?UTF-8?q?=F0=9F=94=A5=20refactor(process.py):=20?= =?UTF-8?q?remove=20unused=20clear=5Fcaches=5Fif=5Fneeded=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔀 refactor(process.py): refactor process_graph_cached function to use Result dataclass for return type and remove unused code --- src/backend/langflow/processing/process.py | 33 ++-------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 10bdbd876..fe180bb7d 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -103,12 +103,6 @@ def get_build_result(data_graph, session_id): return build_sorted_vertices(data_graph, session_id) -def clear_caches_if_needed(clear_cache: bool): - if clear_cache: - build_sorted_vertices.clear_cache() - logger.debug("Cleared cache") - - def load_langchain_object( data_graph: Dict[str, Any], session_id: str ) -> Tuple[Union[Chain, VectorStore], Dict[str, Any], str]: @@ -152,28 +146,6 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): return result -# def process_graph_cached( -# data_graph: Dict[str, Any], -# inputs: Optional[dict] = None, -# clear_cache=False, -# session_id=None, -# ) -> Tuple[Any, str]: -# clear_caches_if_needed(clear_cache) -# # If session_id is provided, load the langchain_object from the session -# # else build the graph and return the result and the new session_id -# langchain_object, artifacts, session_id = load_langchain_object( -# data_graph, session_id -# ) -# processed_inputs = process_inputs(inputs, artifacts) -# result = generate_result(langchain_object, processed_inputs) -# if result: -# # we need to update the cache with the updated langchain_object -# build_sorted_vertices_with_caching.update_cache( -# session_id, (langchain_object, artifacts) -# ) -# return result, session_id - - @dataclass class Result: result: Any @@ -185,9 +157,10 @@ async def process_graph_cached( inputs: Optional[dict] = None, clear_cache=False, session_id=None, -) -> Tuple[Any, str]: - clear_caches_if_needed(clear_cache) +) -> Result: session_manager = get_session_manager() + if clear_cache: + session_manager.clear_session(session_id) if session_id is None: session_id = session_manager.generate_key( session_id=session_id, data_graph=data_graph From 0d9d4856fb0cb589161333b70d2274b771abcdb1 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:35:14 -0300 Subject: [PATCH 114/213] =?UTF-8?q?=F0=9F=90=9B=20fix(celery=5Fapp.py):=20?= =?UTF-8?q?ignore=20type=20error=20for=20Celery=20import=20to=20avoid=20li?= =?UTF-8?q?nting=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(base.py): ignore type error for AsyncResult import to avoid linting issues 🐛 fix(worker.py): ignore type error for SoftTimeLimitExceeded import to avoid linting issues ✨ feat(worker.py): add conditional clearing of session cache based on clear_cache flag to improve performance --- src/backend/langflow/core/celery_app.py | 2 +- src/backend/langflow/graph/vertex/base.py | 2 +- src/backend/langflow/worker.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/core/celery_app.py b/src/backend/langflow/core/celery_app.py index 85223491e..699fc3e14 100644 --- a/src/backend/langflow/core/celery_app.py +++ b/src/backend/langflow/core/celery_app.py @@ -1,4 +1,4 @@ -from celery import Celery +from celery import Celery # type: ignore def make_celery(app_name: str): diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index ccab84179..d8de30ca9 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -76,7 +76,7 @@ class Vertex: def get_task(self): # using the task_id, get the task from celery # and return it - from celery.result import AsyncResult + from celery.result import AsyncResult # type: ignore return AsyncResult(self.task_id) diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 2b66b160c..2077bc3ef 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -1,9 +1,8 @@ from langflow.core.celery_app import celery_app from typing import Any, Dict, Optional, Tuple from typing import TYPE_CHECKING -from celery.exceptions import SoftTimeLimitExceeded +from celery.exceptions import SoftTimeLimitExceeded # type: ignore from langflow.processing.process import ( - clear_caches_if_needed, generate_result, process_inputs, ) @@ -42,8 +41,9 @@ def process_graph_cached_task( session_id=None, ) -> Tuple[Any, str]: initialize_session_manager() - clear_caches_if_needed(clear_cache) session_manager = get_session_manager() + if clear_cache: + session_manager.clear_session(session_id) # Load the graph using SessionManager langchain_object, artifacts = session_manager.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) From 5a6a052b2d28c1a6ec02c05b180c53b0e95daad6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 15:35:30 -0300 Subject: [PATCH 115/213] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20chore(pyproject.to?= =?UTF-8?q?ml):=20upgrade=20types-redis=20to=20version=204.6.0.5=20for=20b?= =?UTF-8?q?etter=20compatibility=20and=20type=20checking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 31 ++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index a77c6710e..94e743b13 100644 --- a/poetry.lock +++ b/poetry.lock @@ -7697,6 +7697,20 @@ files = [ {file = "types_pyasn1-0.4.0.6-py3-none-any.whl", hash = "sha256:dd5fc818864e63a66cd714be0a7a59a493f4a81b87ee9ac978c41f1eaa9a0cef"}, ] +[[package]] +name = "types-pyopenssl" +version = "23.2.0.2" +description = "Typing stubs for pyOpenSSL" +optional = false +python-versions = "*" +files = [ + {file = "types-pyOpenSSL-23.2.0.2.tar.gz", hash = "sha256:6a010dac9ecd42b582d7dd2cc3e9e40486b79b3b64bb2fffba1474ff96af906d"}, + {file = "types_pyOpenSSL-23.2.0.2-py3-none-any.whl", hash = "sha256:19536aa3debfbe25a918cf0d898e9f5fbbe6f3594a429da7914bf331deb1b342"}, +] + +[package.dependencies] +cryptography = ">=35.0.0" + [[package]] name = "types-python-jose" version = "3.3.4.8" @@ -7744,6 +7758,21 @@ files = [ {file = "types_PyYAML-6.0.12.11-py3-none-any.whl", hash = "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d"}, ] +[[package]] +name = "types-redis" +version = "4.6.0.5" +description = "Typing stubs for redis" +optional = false +python-versions = "*" +files = [ + {file = "types-redis-4.6.0.5.tar.gz", hash = "sha256:5f179d10bd3ca995a8134aafcddfc3e12d52b208437c4529ef27e68acb301f38"}, + {file = "types_redis-4.6.0.5-py3-none-any.whl", hash = "sha256:4f662060247a2363c7a8f0b7e52915d68960870ff16a749a891eabcf87ed0be4"}, +] + +[package.dependencies] +cryptography = ">=35.0.0" +types-pyOpenSSL = "*" + [[package]] name = "types-requests" version = "2.31.0.2" @@ -8558,4 +8587,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "c75a76d10e72558d53d8421ff8dee3f741a382f0cf0ea1c44ff0a0ec7a4e78d0" +content-hash = "269c762dad4859168a868eec9eacce6d930e0184f27c07069458f2e667d46a98" diff --git a/pyproject.toml b/pyproject.toml index 4267185d1..75e838b16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,6 +110,7 @@ locust = "^2.16.1" pytest-mock = "^3.11.1" pytest-xdist = "^3.3.1" types-pywin32 = "^306.0.0.4" +types-redis = "^4.6.0.5" [tool.poetry.extras] From 87339b9442b5e3f3e504dde03afa08232be962a9 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Thu, 7 Sep 2023 16:47:08 +0200 Subject: [PATCH 116/213] Fix images and remove depends_on --- deploy/.env.example | 2 +- deploy/docker-compose.yml | 41 ++++----------------------------------- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/deploy/.env.example b/deploy/.env.example index 37af8772a..20413727c 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -30,7 +30,7 @@ LANGFLOW_REDIS_PASSWORD= FLOWER_UNAUTHENTICATED_API=True BROKER_URL=redis://queue:6379/0 RESULT_BACKEND=redis://queue:6379/0 -C_FORCE_ROOT="true # ! Only for development" +C_FORCE_ROOT="true" # Frontend configuration VITE_PROXY_TARGET=http://backend:7860/api/ diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 78623ffa1..a8ad3247f 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -67,18 +67,11 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.middlewares=${STACK_NAME?Variable not set}-www-redirect,${STACK_NAME?Variable not set}-https-redirect backend: - image: "langflow:${TAG-latest}" + image: "ogabrielluiz/langflow:latest" build: context: ../ dockerfile: base.Dockerfile # user: your-non-root-user # Make sure your Dockerfile creates this user - depends_on: - db: - condition: service_healthy - queue: - condition: service_healthy - env_file: - - backend.env # ports: # - 7860 volumes: @@ -98,8 +91,6 @@ services: image: postgres:15.4 volumes: - app-db-data:/var/lib/postgresql/data/pgdata - env_file: - - db.env environment: - PGDATA=/var/lib/postgresql/data/pgdata deploy: @@ -114,11 +105,6 @@ services: networks: - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default - env_file: - - ./pgadmin.env - depends_on: - db: - condition: service_healthy volumes: - ./pgadmin:/var/lib/pgadmin deploy: @@ -143,12 +129,7 @@ services: test: "exit 0" celeryworker: - image: "langflow:${TAG-latest}" - depends_on: - queue: - condition: service_healthy - env_file: - - ./celeryworker.env + image: "ogabrielluiz/langflow:latest" build: context: ../ dockerfile: base.Dockerfile @@ -162,13 +143,6 @@ services: image: mher/flower networks: - default - depends_on: - queue: - condition: service_healthy - celeryworker: - condition: service_healthy - env_file: - - ./flower.env environment: - CELERY_BROKER_URL='redis://$${LANGFLOW_REDIS_PASSWORD:+default:$${LANGFLOW_REDIS_PASSWORD}@}$${LANGFLOW_REDIS_HOST}:$${LANGFLOW_REDIS_PORT}/$${LANGFLOW_REDIS_DB}' - FLOWER_PORT=5555 @@ -187,17 +161,13 @@ services: - traefik.http.services.${STACK_NAME?Variable not set}-flower.loadbalancer.server.port=5555 frontend: + image: "ogabrielluiz/langflow_frontend:latest" # user: your-non-root-user build: context: ../src/frontend dockerfile: Dockerfile args: - BACKEND_URL=http://backend:7860 - depends_on: - backend: - condition: service_healthy - env_file: - - ./frontend.env restart: on-failure deploy: labels: @@ -225,9 +195,6 @@ services: grafana: image: grafana/grafana:8.2.6 - depends_on: - prometheus: - condition: service_healthy # ports: # - 3000:3000 volumes: @@ -246,4 +213,4 @@ volumes: networks: traefik-public: # Allow setting it to false for testing - external: false # ${TRAEFIK_PUBLIC_NETWORK_IS_EXTERNAL-true} + external: false # ${TRAEFIK_PUBLIC_NETWORK_IS_EXTERNAL-true} \ No newline at end of file From 6546e2f5432fea5251cb59b67fa453eedf42cb68 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 11 Sep 2023 11:50:12 -0300 Subject: [PATCH 117/213] =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.yml?= =?UTF-8?q?):=20update=20flower=20service=20image=20to=20"ogabrielluiz/lan?= =?UTF-8?q?gflow:latest"=20for=20consistency=20and=20reliability=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.yml):=20add=20build=20confi?= =?UTF-8?q?guration=20to=20flower=20service=20to=20use=20base.Dockerfile?= =?UTF-8?q?=20for=20building=20the=20image=20=F0=9F=94=A7=20chore(docker-c?= =?UTF-8?q?ompose.yml):=20update=20command=20for=20flower=20service=20to?= =?UTF-8?q?=20use=20the=20correct=20broker=20URL=20and=20set=20the=20port?= =?UTF-8?q?=20to=205555=20=F0=9F=94=A7=20chore(docker-compose.yml):=20remo?= =?UTF-8?q?ve=20unnecessary=20comment=20at=20the=20end=20of=20the=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index a8ad3247f..23fd4fc15 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -140,12 +140,16 @@ services: replicas: 1 flower: - image: mher/flower + image: "ogabrielluiz/langflow:latest" networks: - default + build: + context: ../ + dockerfile: base.Dockerfile environment: - - CELERY_BROKER_URL='redis://$${LANGFLOW_REDIS_PASSWORD:+default:$${LANGFLOW_REDIS_PASSWORD}@}$${LANGFLOW_REDIS_HOST}:$${LANGFLOW_REDIS_PORT}/$${LANGFLOW_REDIS_DB}' - FLOWER_PORT=5555 + + command: /bin/sh -c "celery -A langflow.worker.celery_app --broker=${BROKER_URL?Variable not set} flower --port=5555" deploy: labels: - traefik.enable=true @@ -213,4 +217,4 @@ volumes: networks: traefik-public: # Allow setting it to false for testing - external: false # ${TRAEFIK_PUBLIC_NETWORK_IS_EXTERNAL-true} \ No newline at end of file + external: false # ${TRAEFIK_PUBLIC_NETWORK_IS_EXTERNAL-true} From 6e529903226b2ba651ba34cb59946df366079875 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 11 Sep 2023 12:16:29 -0300 Subject: [PATCH 118/213] =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.yml?= =?UTF-8?q?):=20add=20support=20for=20loading=20environment=20variables=20?= =?UTF-8?q?from=20.env=20file=20for=20all=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the proxy service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the backend service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the pgadmin service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the queue service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the celeryworker service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the flower service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the frontend service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the prometheus service 🔧 chore(docker-compose.yml): add support for loading environment variables from .env file for the grafana service --- deploy/docker-compose.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 23fd4fc15..8d79b3124 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -3,6 +3,8 @@ version: "3.8" services: proxy: image: traefik:v3.0 + env_file: + - .env networks: - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default @@ -71,9 +73,8 @@ services: build: context: ../ dockerfile: base.Dockerfile - # user: your-non-root-user # Make sure your Dockerfile creates this user - # ports: - # - 7860 + env_file: + - .env volumes: - ../:/app - ./startup-backend.sh:/startup-backend.sh # Ensure the paths match @@ -99,6 +100,8 @@ services: - node.labels.${STACK_NAME?Variable not set}.app-db-data == true healthcheck: test: "exit 0" + env_file: + - .env pgadmin: image: dpage/pgadmin4 @@ -107,6 +110,8 @@ services: - default volumes: - ./pgadmin:/var/lib/pgadmin + env_file: + - .env deploy: labels: - traefik.enable=true @@ -123,6 +128,8 @@ services: queue: image: redis:6.2.5 + env_file: + - .env # ports: # - 6379:6379 healthcheck: @@ -130,6 +137,8 @@ services: celeryworker: image: "ogabrielluiz/langflow:latest" + env_file: + - .env build: context: ../ dockerfile: base.Dockerfile @@ -141,6 +150,8 @@ services: flower: image: "ogabrielluiz/langflow:latest" + env_file: + - .env networks: - default build: @@ -166,6 +177,8 @@ services: frontend: image: "ogabrielluiz/langflow_frontend:latest" + env_file: + - .env # user: your-non-root-user build: context: ../src/frontend @@ -182,6 +195,8 @@ services: prometheus: image: prom/prometheus:v2.37.9 + env_file: + - .env volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml command: @@ -199,6 +214,8 @@ services: grafana: image: grafana/grafana:8.2.6 + env_file: + - .env # ports: # - 3000:3000 volumes: From e557c0d234c72dab30c626e12f9bf3ae51f5d5d9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 11 Sep 2023 14:01:48 -0300 Subject: [PATCH 119/213] =?UTF-8?q?=F0=9F=94=A7=20chore(.env.example):=20a?= =?UTF-8?q?dd=20POSTGRES=5FPORT=20variable=20to=20specify=20the=20port=20f?= =?UTF-8?q?or=20PostgreSQL=20connection=20=F0=9F=94=A7=20chore(startup-bac?= =?UTF-8?q?kend.sh):=20modify=20startup=20script=20to=20start=20the=20back?= =?UTF-8?q?end=20in=20development=20or=20production=20mode=20based=20on=20?= =?UTF-8?q?the=20ENVIRONMENT=20variable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/.env.example | 2 ++ deploy/startup-backend.sh | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/deploy/.env.example b/deploy/.env.example index 20413727c..ab24e0114 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -1,5 +1,6 @@ DOMAIN=localhost STACK_NAME=langflow-stack +ENVIRONMENT=development TRAEFIK_PUBLIC_NETWORK=traefik-public TRAEFIK_TAG=langflow-traefik @@ -19,6 +20,7 @@ LOG_LEVEL=debug POSTGRES_USER=langflow POSTGRES_PASSWORD=langflow POSTGRES_DB=langflow +POSTGRES_PORT=5432 # Flower configuration LANGFLOW_CACHE_TYPE=redis diff --git a/deploy/startup-backend.sh b/deploy/startup-backend.sh index c8a634f65..75c8002a5 100755 --- a/deploy/startup-backend.sh +++ b/deploy/startup-backend.sh @@ -2,5 +2,16 @@ export LANGFLOW_DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}" + # Your command to start the backend -exec python -m uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --log-level ${LOG_LEVEL:-info} --workers -1 + +# If the ENVIRONMENT variable is set to "development", then start the backend in development mode +# else start the backend in production mode with guvicorn +if [ "$ENVIRONMENT" = "development" ]; then + echo "Starting backend in development mode" + exec python -m uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --log-level ${LOG_LEVEL:-info} --workers 2 --reload +else + echo "Starting backend in production mode" + exec langflow run --host 0.0.0.0 --port 7860 --log-level ${LOG_LEVEL:-info} --workers -1 --backend-only +fi + From c2520554b22719e15eac3e19ff907e6b6bdc3770 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Tue, 12 Sep 2023 11:40:03 +0200 Subject: [PATCH 120/213] =?UTF-8?q?=F0=9F=90=9B=20fix(docker-compose.yml):?= =?UTF-8?q?=20remove=20unnecessary=20variable=20check=20in=20node=20placem?= =?UTF-8?q?ent=20constraints=20to=20fix=20deployment=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 8d79b3124..c6a9db73c 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -97,7 +97,7 @@ services: deploy: placement: constraints: - - node.labels.${STACK_NAME?Variable not set}.app-db-data == true + - node.labels.app-db-data == true healthcheck: test: "exit 0" env_file: From 1ca839899a0317dc6a48d481fb3555219bc24917 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Tue, 12 Sep 2023 11:40:33 +0200 Subject: [PATCH 121/213] =?UTF-8?q?=F0=9F=94=A7=20chore(main.tf):=20use=20?= =?UTF-8?q?variable=20for=20AWS=20region=20to=20improve=20flexibility=20an?= =?UTF-8?q?d=20reusability=20=F0=9F=94=A7=20chore(main.tf):=20use=20variab?= =?UTF-8?q?les=20for=20instance=20type,=20manager=20count,=20and=20worker?= =?UTF-8?q?=20count=20to=20improve=20configurability=20and=20scalability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/scripts/terraform/main.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/scripts/terraform/main.tf b/deploy/scripts/terraform/main.tf index 76c8dcb58..8c103d00b 100644 --- a/deploy/scripts/terraform/main.tf +++ b/deploy/scripts/terraform/main.tf @@ -7,7 +7,7 @@ terraform { } provider "aws" { - region = "us-east-1" # Choose the region as needed + region = var.region # Choose the region as needed } module "docker-swarm" { @@ -16,9 +16,9 @@ module "docker-swarm" { vpc_id = aws_vpc.swarm-vpc.id subnet_id = aws_subnet.swarm-public-subnet.id security_group = aws_security_group.swarm-sg.id - instance_type = "t2.micro" # Choose the instance type as needed - manager_count = 1 - worker_count = 10 # This is the number of services in the docker-compose.yml file + instance_type = var.instance_type # Choose the instance type as needed + manager_count = var.manager_count + worker_count = var.worker_count # This is the number of services in the docker-compose.yml file } resource "aws_key_pair" "swarm-key" { From 4f6aeefd96cfbb339a86ce291f6ee46b848fca60 Mon Sep 17 00:00:00 2001 From: Matheus Jacques Date: Tue, 12 Sep 2023 11:42:58 +0200 Subject: [PATCH 122/213] =?UTF-8?q?=F0=9F=94=A7=20chore(main.tf):=20add=20?= =?UTF-8?q?label=20to=20a=20random=20worker=20node=20to=20ensure=20data=20?= =?UTF-8?q?volume=20is=20created=20=F0=9F=94=A7=20chore(main.tf):=20deploy?= =?UTF-8?q?=20the=20langflow=5Fstack=20using=20docker=20stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/scripts/terraform/modules/docker-swarm/main.tf | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/deploy/scripts/terraform/modules/docker-swarm/main.tf b/deploy/scripts/terraform/modules/docker-swarm/main.tf index bc05a4e24..c29f572e9 100644 --- a/deploy/scripts/terraform/modules/docker-swarm/main.tf +++ b/deploy/scripts/terraform/modules/docker-swarm/main.tf @@ -38,9 +38,20 @@ resource "aws_instance" "manager" { echo 'docker swarm join-token worker -q' > get_token.sh chmod +x get_token.sh timeout 5m bash -c "while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; ./get_token.sh; } | nc -l 8080; done" & + sleep 10 + # Clone the repo and start the stack git clone https://github.com/logspace-ai/langflow.git + cd /langflow git checkout celery + cd /langflow/deploy + + sudo cp .env.example .env + + # Add the label to random worker node to ensure that the data volume is created on the same node + docker node update --label-add app-db-data=true $(docker node ls --format '{{.Hostname}}' --filter role=worker | head -n 1) + + env $(cat .env | grep ^[A-Z] | xargs) docker stack deploy --compose-file docker-compose.yml langflow_stack EOT From 651595932ce8cd873ad1771a8b88a3faf5d4486a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 15 Sep 2023 17:41:39 -0300 Subject: [PATCH 123/213] Change all Services names from Manager to Service --- src/backend/langflow/__init__.py | 4 +- src/backend/langflow/__main__.py | 26 ++++---- src/backend/langflow/api/v1/chat.py | 62 +++++++++---------- src/backend/langflow/api/v1/endpoints.py | 29 ++++----- src/backend/langflow/api/v1/flows.py | 6 +- src/backend/langflow/api/v1/login.py | 6 +- .../langflow/components/io/ChatOutput.py | 39 ++++++++++++ src/backend/langflow/interface/agents/base.py | 8 +-- src/backend/langflow/interface/base.py | 6 +- src/backend/langflow/interface/chains/base.py | 8 +-- .../interface/custom/custom_component.py | 14 ++--- .../interface/document_loaders/base.py | 8 +-- .../langflow/interface/embeddings/base.py | 8 +-- src/backend/langflow/interface/llms/base.py | 8 +-- .../langflow/interface/memories/base.py | 8 +-- .../langflow/interface/output_parsers/base.py | 8 +-- .../langflow/interface/prompts/base.py | 8 +-- .../langflow/interface/retrievers/base.py | 8 +-- .../langflow/interface/text_splitters/base.py | 8 +-- .../langflow/interface/toolkits/base.py | 6 +- src/backend/langflow/interface/tools/base.py | 8 +-- .../langflow/interface/utilities/base.py | 8 +-- src/backend/langflow/interface/utils.py | 8 +-- .../langflow/interface/vector_store/base.py | 8 +-- src/backend/langflow/processing/process.py | 14 ++--- src/backend/langflow/services/__init__.py | 4 +- src/backend/langflow/services/auth/factory.py | 12 ++-- src/backend/langflow/services/auth/service.py | 10 +-- src/backend/langflow/services/auth/utils.py | 50 +++++++-------- src/backend/langflow/services/cache/base.py | 4 +- .../langflow/services/cache/factory.py | 24 +++---- .../langflow/services/cache/manager.py | 6 +- src/backend/langflow/services/chat/cache.py | 6 +- src/backend/langflow/services/chat/factory.py | 10 +-- src/backend/langflow/services/chat/manager.py | 18 +++--- .../langflow/services/database/factory.py | 16 ++--- .../langflow/services/database/manager.py | 18 +++--- .../langflow/services/database/utils.py | 20 +++--- src/backend/langflow/services/manager.py | 58 ++++++++--------- src/backend/langflow/services/schema.py | 14 ++--- .../langflow/services/session/factory.py | 12 ++-- .../langflow/services/session/manager.py | 20 +++--- .../langflow/services/settings/factory.py | 10 +-- .../langflow/services/settings/manager.py | 6 +- src/backend/langflow/services/task/factory.py | 10 +-- src/backend/langflow/services/task/manager.py | 6 +- src/backend/langflow/services/utils.py | 42 ++++++------- src/backend/langflow/utils/util.py | 4 +- src/backend/langflow/worker.py | 16 ++--- tests/conftest.py | 6 +- tests/test_agents_template.py | 6 +- tests/test_cache_manager.py | 58 ++++++++--------- tests/test_cli.py | 4 +- tests/test_endpoints.py | 14 ++--- tests/test_llms_template.py | 6 +- tests/test_process.py | 24 +++---- tests/test_prompts_template.py | 6 +- tests/test_user.py | 10 +-- tests/test_vectorstore_template.py | 6 +- tests/test_websocket.py | 8 +-- 60 files changed, 454 insertions(+), 414 deletions(-) create mode 100644 src/backend/langflow/components/io/ChatOutput.py diff --git a/src/backend/langflow/__init__.py b/src/backend/langflow/__init__.py index f6eb836cc..d3afbb4af 100644 --- a/src/backend/langflow/__init__.py +++ b/src/backend/langflow/__init__.py @@ -1,7 +1,7 @@ from importlib import metadata # Deactivate cache manager for now -# from langflow.services.cache import cache_manager +# from langflow.services.cache import cache_service from langflow.processing.process import load_flow_from_json from langflow.interface.custom.custom_component import CustomComponent @@ -12,4 +12,4 @@ except metadata.PackageNotFoundError: __version__ = "" del metadata # optional, avoids polluting the results of dir(__package__) -__all__ = ["load_flow_from_json", "cache_manager", "CustomComponent"] +__all__ = ["load_flow_from_json", "cache_service", "CustomComponent"] diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index a08ae9fb0..bcc26fe8b 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -2,8 +2,8 @@ import sys import time import httpx from langflow.services.database.utils import session_getter -from langflow.services.manager import initialize_services, initialize_settings_manager -from langflow.services.utils import get_db_manager, get_settings_manager +from langflow.services.manager import initialize_services, initialize_settings_service +from langflow.services.utils import get_db_service, get_settings_service from multiprocess import Process, cpu_count # type: ignore import platform @@ -63,20 +63,20 @@ def update_settings( """Update the settings from a config file.""" # Check for database_url in the environment variables - initialize_settings_manager() - settings_manager = get_settings_manager() + initialize_settings_service() + settings_service = get_settings_service() if config: logger.debug(f"Loading settings from {config}") - settings_manager.settings.update_from_yaml(config, dev=dev) + settings_service.settings.update_from_yaml(config, dev=dev) if remove_api_keys: logger.debug(f"Setting remove_api_keys to {remove_api_keys}") - settings_manager.settings.update_settings(REMOVE_API_KEYS=remove_api_keys) + settings_service.settings.update_settings(REMOVE_API_KEYS=remove_api_keys) if cache: logger.debug(f"Setting cache to {cache}") - settings_manager.settings.update_settings(CACHE=cache) + settings_service.settings.update_settings(CACHE=cache) if components_path: logger.debug(f"Adding component path {components_path}") - settings_manager.settings.update_settings(COMPONENTS_PATH=components_path) + settings_service.settings.update_settings(COMPONENTS_PATH=components_path) def serve_on_jcloud(): @@ -352,8 +352,8 @@ def superuser( ), ): initialize_services() - db_manager = get_db_manager() - with session_getter(db_manager) as session: + db_service = get_db_service() + with session_getter(db_service) as session: from langflow.services.auth.utils import create_super_user if create_super_user(db=session, username=username, password=password): @@ -374,10 +374,10 @@ def superuser( @app.command() def migration(test: bool = typer.Option(False, help="Run migrations in test mode.")): initialize_services() - db_manager = get_db_manager() + db_service = get_db_service() if not test: - db_manager.run_migrations() - results = db_manager.run_migrations_test() + db_service.run_migrations() + results = db_service.run_migrations_test() display_results(results) diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index 4e7807ab8..0740b7517 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -13,12 +13,12 @@ from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, St from langflow.graph.graph.base import Graph from langflow.services.auth.utils import get_current_active_user, get_current_user -from langflow.services.utils import get_cache_manager, get_session +from langflow.services.utils import get_cache_service, get_session from loguru import logger -from langflow.services.utils import get_chat_manager +from langflow.services.utils import get_chat_service from sqlmodel import Session -from langflow.services.chat.manager import ChatManager -from langflow.services.cache.manager import BaseCacheManager +from langflow.services.chat.manager import ChatService +from langflow.services.cache.manager import BaseCacheService router = APIRouter(tags=["Chat"]) @@ -30,7 +30,7 @@ async def chat( websocket: WebSocket, token: str = Query(...), db: Session = Depends(get_session), - chat_manager: "ChatManager" = Depends(get_chat_manager), + chat_service: "ChatService" = Depends(get_chat_service), ): """Websocket endpoint for chat.""" try: @@ -45,8 +45,8 @@ async def chat( code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized" ) - if client_id in chat_manager.cache_manager: - await chat_manager.handle_websocket(client_id, websocket) + if client_id in chat_service.cache_service: + await chat_service.handle_websocket(client_id, websocket) else: # We accept the connection but close it immediately # if the flow is not built yet @@ -71,8 +71,8 @@ async def init_build( graph_data: dict, flow_id: str, current_user=Depends(get_current_active_user), - chat_manager: "ChatManager" = Depends(get_chat_manager), - cache_manager: "BaseCacheManager" = Depends(get_cache_manager), + chat_service: "ChatService" = Depends(get_chat_service), + cache_service: "BaseCacheService" = Depends(get_cache_service), ): """Initialize the build by storing graph data and returning a unique session ID.""" try: @@ -80,17 +80,17 @@ async def init_build( raise ValueError("No ID provided") # Check if already building if ( - flow_id in cache_manager - and isinstance(cache_manager[flow_id], dict) - and cache_manager[flow_id].get("status") == BuildStatus.IN_PROGRESS + flow_id in cache_service + and isinstance(cache_service[flow_id], dict) + and cache_service[flow_id].get("status") == BuildStatus.IN_PROGRESS ): return InitResponse(flowId=flow_id) # Delete from cache if already exists - if flow_id in chat_manager.cache_manager: - chat_manager.cache_manager.delete(flow_id) + if flow_id in chat_service.cache_service: + chat_service.cache_service.delete(flow_id) logger.debug(f"Deleted flow {flow_id} from cache") - cache_manager[flow_id] = { + cache_service[flow_id] = { "graph_data": graph_data, "status": BuildStatus.STARTED, "user_id": current_user.id, @@ -104,13 +104,13 @@ async def init_build( @router.get("/build/{flow_id}/status", response_model=BuiltResponse) async def build_status( - flow_id: str, cache_manager: "BaseCacheManager" = Depends(get_cache_manager) + flow_id: str, cache_service: "BaseCacheService" = Depends(get_cache_service) ): - """Check the flow_id is in the cache_manager.""" + """Check the flow_id is in the cache_service.""" try: built = ( - flow_id in cache_manager - and cache_manager[flow_id]["status"] == BuildStatus.SUCCESS + flow_id in cache_service + and cache_service[flow_id]["status"] == BuildStatus.SUCCESS ) return BuiltResponse( @@ -125,8 +125,8 @@ async def build_status( @router.get("/build/stream/{flow_id}", response_class=StreamingResponse) async def stream_build( flow_id: str, - chat_manager: "ChatManager" = Depends(get_chat_manager), - cache_manager: "BaseCacheManager" = Depends(get_cache_manager), + chat_service: "ChatService" = Depends(get_chat_service), + cache_service: "BaseCacheService" = Depends(get_cache_service), ): """Stream the build process based on stored flow data.""" @@ -134,18 +134,18 @@ async def stream_build( final_response = {"end_of_stream": True} artifacts = {} try: - if flow_id not in cache_manager: + if flow_id not in cache_service: error_message = "Invalid session ID" yield str(StreamData(event="error", data={"error": error_message})) return - if cache_manager[flow_id].get("status") == BuildStatus.IN_PROGRESS: + if cache_service[flow_id].get("status") == BuildStatus.IN_PROGRESS: error_message = "Already building" yield str(StreamData(event="error", data={"error": error_message})) return - graph_data = cache_manager[flow_id].get("graph_data") - cache_manager[flow_id]["user_id"] + graph_data = cache_service[flow_id].get("graph_data") + cache_service[flow_id]["user_id"] if not graph_data: error_message = "No data provided" @@ -158,7 +158,7 @@ async def stream_build( graph = Graph.from_payload(graph_data) number_of_nodes = len(graph.nodes) - cache_manager[flow_id]["status"] = BuildStatus.IN_PROGRESS + cache_service[flow_id]["status"] = BuildStatus.IN_PROGRESS for i, vertex in enumerate(graph.generator_build(), 1): try: @@ -185,7 +185,7 @@ async def stream_build( logger.exception(exc) params = str(exc) valid = False - cache_manager[flow_id]["status"] = BuildStatus.FAILURE + cache_service[flow_id]["status"] = BuildStatus.FAILURE response = { "valid": valid, @@ -209,14 +209,14 @@ async def stream_build( "handle_keys": [], } yield str(StreamData(event="message", data=input_keys_response)) - chat_manager.set_cache(flow_id, langchain_object) + chat_service.set_cache(flow_id, langchain_object) # We need to reset the chat history - chat_manager.chat_history.empty_history(flow_id) - cache_manager[flow_id]["status"] = BuildStatus.SUCCESS + chat_service.chat_history.empty_history(flow_id) + cache_service[flow_id]["status"] = BuildStatus.SUCCESS except Exception as exc: logger.exception(exc) logger.error("Error while building the flow: %s", exc) - cache_manager[flow_id]["status"] = BuildStatus.FAILURE + cache_service[flow_id]["status"] = BuildStatus.FAILURE yield str(StreamData(event="error", data={"error": str(exc)})) finally: yield str(StreamData(event="message", data=final_response)) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 47c815bbe..d7fed1a70 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Annotated, Any, Optional, Union +from typing import TYPE_CHECKING, Annotated, Any, Optional, Union from langflow.services.auth.utils import api_key_security, get_current_active_user @@ -7,7 +7,7 @@ from langflow.services.cache.utils import save_uploaded_file from langflow.services.database.models.flow import Flow from langflow.processing.process import process_graph_cached, process_tweaks from langflow.services.database.models.user.user import User -from langflow.services.utils import get_settings_manager, get_task_manager +from langflow.services.utils import get_settings_service, get_task_service from loguru import logger from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status import sqlalchemy as sa @@ -33,7 +33,8 @@ from langflow.services.utils import get_session from langflow.worker import process_graph_cached_task from sqlmodel import Session -from langflow.services.task.manager import TaskManager +if TYPE_CHECKING: + from langflow.services.task.manager import TaskService # build router router = APIRouter(tags=["Base"]) @@ -41,21 +42,21 @@ router = APIRouter(tags=["Base"]) @router.get("/all", dependencies=[Depends(get_current_active_user)]) def get_all( - settings_manager=Depends(get_settings_manager), + settings_service=Depends(get_settings_service), ): logger.debug("Building langchain types dict") native_components = build_langchain_types_dict() # custom_components is a list of dicts # need to merge all the keys into one dict custom_components_from_file: dict[str, Any] = {} - if settings_manager.settings.COMPONENTS_PATH: + if settings_service.settings.COMPONENTS_PATH: logger.info( - f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}" + f"Building custom components from {settings_service.settings.COMPONENTS_PATH}" ) custom_component_dicts = [] processed_paths = [] - for path in settings_manager.settings.COMPONENTS_PATH: + for path in settings_service.settings.COMPONENTS_PATH: if str(path) in processed_paths: continue custom_component_dict = build_langchain_custom_component_list_from_path( @@ -99,7 +100,7 @@ async def process_flow( tweaks: Optional[dict] = None, clear_cache: Annotated[bool, Body(embed=True)] = False, # noqa: F821 session_id: Annotated[Union[None, str], Body(embed=True)] = None, # noqa: F821 - task_manager: "TaskManager" = Depends(get_task_manager), + task_service: "TaskService" = Depends(get_task_service), api_key_user: User = Depends(api_key_security), sync: Annotated[bool, Body(embed=True)] = True, # noqa: F821 ): @@ -133,9 +134,9 @@ async def process_flow( except Exception as exc: logger.error(f"Error processing tweaks: {exc}") if sync: - task_id, result = await task_manager.launch_and_await_task( + task_id, result = await task_service.launch_and_await_task( process_graph_cached_task - if task_manager.use_celery + if task_service.use_celery else process_graph_cached, graph_data, inputs, @@ -145,9 +146,9 @@ async def process_flow( task_result = result.result session_id = result.session_id else: - task_id, task = await task_manager.launch_task( + task_id, task = await task_service.launch_task( process_graph_cached_task - if task_manager.use_celery + if task_service.use_celery else process_graph_cached, graph_data, inputs, @@ -180,8 +181,8 @@ async def process_flow( @router.get("/task/{task_id}/status", response_model=TaskStatusResponse) async def get_task_status(task_id: str): - task_manager = get_task_manager() - task = task_manager.get_task(task_id) + task_service = get_task_service() + task = task_service.get_task(task_id) if task is None: raise HTTPException(status_code=404, detail="Task not found") return TaskStatusResponse( diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index be65048d4..be49ad836 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -13,7 +13,7 @@ from langflow.services.database.models.flow import ( ) from langflow.services.database.models.user.user import User from langflow.services.utils import get_session -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service import orjson from sqlmodel import Session from fastapi import APIRouter, Depends, HTTPException @@ -83,7 +83,7 @@ def update_flow( flow_id: UUID, flow: FlowUpdate, current_user: User = Depends(get_current_active_user), - settings_manager=Depends(get_settings_manager), + settings_service=Depends(get_settings_service), ): """Update a flow.""" @@ -91,7 +91,7 @@ def update_flow( if not db_flow: raise HTTPException(status_code=404, detail="Flow not found") flow_data = flow.dict(exclude_unset=True) - if settings_manager.settings.REMOVE_API_KEYS: + if settings_service.settings.REMOVE_API_KEYS: flow_data = remove_api_keys(flow_data) for key, value in flow_data.items(): if value is not None: diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index 4241b8d47..e67580b9d 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -12,7 +12,7 @@ from langflow.services.auth.utils import ( get_current_active_user, ) -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service router = APIRouter(tags=["Login"]) @@ -35,9 +35,9 @@ async def login_to_get_access_token( @router.get("/auto_login") async def auto_login( - db: Session = Depends(get_session), settings_manager=Depends(get_settings_manager) + db: Session = Depends(get_session), settings_service=Depends(get_settings_service) ): - if settings_manager.auth_settings.AUTO_LOGIN: + if settings_service.auth_settings.AUTO_LOGIN: return create_user_longterm_token(db) raise HTTPException( diff --git a/src/backend/langflow/components/io/ChatOutput.py b/src/backend/langflow/components/io/ChatOutput.py new file mode 100644 index 000000000..48fec75bc --- /dev/null +++ b/src/backend/langflow/components/io/ChatOutput.py @@ -0,0 +1,39 @@ +from typing import Optional, Text +from langflow.api.v1.schemas import ChatMessage +from langflow.services.utils import get_chat_service +from langflow import CustomComponent +from anyio.from_thread import start_blocking_portal +from loguru import logger + + +class ChatOutput(CustomComponent): + display_name = "Chat Output" + description = "Used to send a message to the chat." + + field_config = { + "code": { + "show": False, + } + } + + def build_config(self): + return {"message": {"input_types": ["Text"]}} + + def build(self, message: Optional[Text], is_ai: bool = False) -> Text: + if not message: + return "" + try: + chat_service = get_chat_service() + chat_message = ChatMessage(message=message, is_bot=is_ai) + # send_message is a coroutine + # run in a thread safe manner + with start_blocking_portal() as portal: + portal.call(chat_service.send_message, chat_message) + chat_service.chat_history.add_message( + chat_service.cache_service.current_client_id, chat_message + ) + except Exception as exc: + logger.exception(exc) + logger.debug(f"Error sending message to chat: {exc}") + self.repr_value = message + return message diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index 574264e47..16df6d98f 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -5,7 +5,7 @@ from langchain.agents import types from langflow.custom.customs import get_custom_nodes from langflow.interface.agents.custom import CUSTOM_AGENTS from langflow.interface.base import LangChainTypeCreator -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.agents import AgentFrontendNode from loguru import logger @@ -54,7 +54,7 @@ class AgentCreator(LangChainTypeCreator): # Now this is a generator def to_list(self) -> List[str]: names = [] - settings_manager = get_settings_manager() + settings_service = get_settings_service() for _, agent in self.type_to_loader_dict.items(): agent_name = ( agent.function_name() @@ -62,8 +62,8 @@ class AgentCreator(LangChainTypeCreator): else agent.__name__ ) if ( - agent_name in settings_manager.settings.AGENTS - or settings_manager.settings.DEV + agent_name in settings_service.settings.AGENTS + or settings_service.settings.DEV ): names.append(agent_name) return names diff --git a/src/backend/langflow/interface/base.py b/src/backend/langflow/interface/base.py index b006a3174..f622cf478 100644 --- a/src/backend/langflow/interface/base.py +++ b/src/backend/langflow/interface/base.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Type, Union from langchain.chains.base import Chain from langchain.agents import AgentExecutor -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from pydantic import BaseModel from langflow.template.field.base import TemplateField @@ -27,11 +27,11 @@ class LangChainTypeCreator(BaseModel, ABC): @property def docs_map(self) -> Dict[str, str]: """A dict with the name of the component as key and the documentation link as value.""" - settings_manager = get_settings_manager() + settings_service = get_settings_service() if self.name_docs_dict is None: try: type_settings = getattr( - settings_manager.settings, self.type_name.upper() + settings_service.settings, self.type_name.upper() ) self.name_docs_dict = { name: value_dict["documentation"] diff --git a/src/backend/langflow/interface/chains/base.py b/src/backend/langflow/interface/chains/base.py index 755ac82dd..c18ff34ab 100644 --- a/src/backend/langflow/interface/chains/base.py +++ b/src/backend/langflow/interface/chains/base.py @@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Type from langflow.custom.customs import get_custom_nodes from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.chains import ChainFrontendNode from loguru import logger @@ -31,7 +31,7 @@ class ChainCreator(LangChainTypeCreator): @property def type_to_loader_dict(self) -> Dict: if self.type_dict is None: - settings_manager = get_settings_manager() + settings_service = get_settings_service() self.type_dict: dict[str, Any] = { chain_name: import_class(f"langchain.chains.{chain_name}") for chain_name in chains.__all__ @@ -45,8 +45,8 @@ class ChainCreator(LangChainTypeCreator): self.type_dict = { name: chain for name, chain in self.type_dict.items() - if name in settings_manager.settings.CHAINS - or settings_manager.settings.DEV + if name in settings_service.settings.CHAINS + or settings_service.settings.DEV } return self.type_dict diff --git a/src/backend/langflow/interface/custom/custom_component.py b/src/backend/langflow/interface/custom/custom_component.py index 1c96ea591..ef6761081 100644 --- a/src/backend/langflow/interface/custom/custom_component.py +++ b/src/backend/langflow/interface/custom/custom_component.py @@ -4,7 +4,7 @@ from fastapi import HTTPException from langflow.interface.custom.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES from langflow.interface.custom.component import Component from langflow.interface.custom.directory_reader import DirectoryReader -from langflow.services.utils import get_db_manager +from langflow.services.utils import get_db_service from langflow.interface.custom.utils import extract_inner_type from langflow.utils import validate @@ -179,8 +179,8 @@ class CustomComponent(Component, extra=Extra.allow): from langflow.processing.process import build_sorted_vertices from langflow.processing.process import process_tweaks - db_manager = get_db_manager() - with session_getter(db_manager) as session: + db_service = get_db_service() + with session_getter(db_service) as session: graph_data = flow.data if (flow := session.get(Flow, flow_id)) else None if not graph_data: raise ValueError(f"Flow {flow_id} not found") @@ -193,8 +193,8 @@ class CustomComponent(Component, extra=Extra.allow): raise ValueError("Session is invalid") try: get_session = get_session or session_getter - db_manager = get_db_manager() - with get_session(db_manager) as session: + db_service = get_db_service() + with get_session(db_service) as session: flows = session.query(Flow).filter(Flow.user_id == self.user_id).all() return flows except Exception as e: @@ -209,8 +209,8 @@ class CustomComponent(Component, extra=Extra.allow): get_session: Optional[Callable] = None, ) -> Flow: get_session = get_session or session_getter - db_manager = get_db_manager() - with get_session(db_manager) as session: + db_service = get_db_service() + with get_session(db_service) as session: if flow_id: flow = session.query(Flow).get(flow_id) elif flow_name: diff --git a/src/backend/langflow/interface/document_loaders/base.py b/src/backend/langflow/interface/document_loaders/base.py index a2c147e16..84c84ee55 100644 --- a/src/backend/langflow/interface/document_loaders/base.py +++ b/src/backend/langflow/interface/document_loaders/base.py @@ -1,7 +1,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode from langflow.interface.custom_lists import documentloaders_type_to_cls_dict @@ -31,12 +31,12 @@ class DocumentLoaderCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ documentloader.__name__ for documentloader in self.type_to_loader_dict.values() - if documentloader.__name__ in settings_manager.settings.DOCUMENTLOADERS - or settings_manager.settings.DEV + if documentloader.__name__ in settings_service.settings.DOCUMENTLOADERS + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/interface/embeddings/base.py b/src/backend/langflow/interface/embeddings/base.py index 1063d10d1..72d9a4cdb 100644 --- a/src/backend/langflow/interface/embeddings/base.py +++ b/src/backend/langflow/interface/embeddings/base.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import embedding_type_to_cls_dict -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode @@ -33,12 +33,12 @@ class EmbeddingCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ embedding.__name__ for embedding in self.type_to_loader_dict.values() - if embedding.__name__ in settings_manager.settings.EMBEDDINGS - or settings_manager.settings.DEV + if embedding.__name__ in settings_service.settings.EMBEDDINGS + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/interface/llms/base.py b/src/backend/langflow/interface/llms/base.py index 87e4937cf..74ec78af6 100644 --- a/src/backend/langflow/interface/llms/base.py +++ b/src/backend/langflow/interface/llms/base.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import llm_type_to_cls_dict -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.llms import LLMFrontendNode from loguru import logger @@ -34,12 +34,12 @@ class LLMCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ llm.__name__ for llm in self.type_to_loader_dict.values() - if llm.__name__ in settings_manager.settings.LLMS - or settings_manager.settings.DEV + if llm.__name__ in settings_service.settings.LLMS + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/interface/memories/base.py b/src/backend/langflow/interface/memories/base.py index 61c6cc430..fa3576305 100644 --- a/src/backend/langflow/interface/memories/base.py +++ b/src/backend/langflow/interface/memories/base.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import memory_type_to_cls_dict -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.memories import MemoryFrontendNode @@ -49,12 +49,12 @@ class MemoryCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ memory.__name__ for memory in self.type_to_loader_dict.values() - if memory.__name__ in settings_manager.settings.MEMORIES - or settings_manager.settings.DEV + if memory.__name__ in settings_service.settings.MEMORIES + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/interface/output_parsers/base.py b/src/backend/langflow/interface/output_parsers/base.py index b6eb36a0e..93d4eeeda 100644 --- a/src/backend/langflow/interface/output_parsers/base.py +++ b/src/backend/langflow/interface/output_parsers/base.py @@ -4,7 +4,7 @@ from langchain import output_parsers from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode from loguru import logger @@ -24,7 +24,7 @@ class OutputParserCreator(LangChainTypeCreator): @property def type_to_loader_dict(self) -> Dict: if self.type_dict is None: - settings_manager = get_settings_manager() + settings_service = get_settings_service() self.type_dict = { output_parser_name: import_class( f"langchain.output_parsers.{output_parser_name}" @@ -35,8 +35,8 @@ class OutputParserCreator(LangChainTypeCreator): self.type_dict = { name: output_parser for name, output_parser in self.type_dict.items() - if name in settings_manager.settings.OUTPUT_PARSERS - or settings_manager.settings.DEV + if name in settings_service.settings.OUTPUT_PARSERS + or settings_service.settings.DEV } return self.type_dict diff --git a/src/backend/langflow/interface/prompts/base.py b/src/backend/langflow/interface/prompts/base.py index 70818429e..01ac40ab6 100644 --- a/src/backend/langflow/interface/prompts/base.py +++ b/src/backend/langflow/interface/prompts/base.py @@ -5,7 +5,7 @@ from langchain import prompts from langflow.custom.customs import get_custom_nodes from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.prompts import PromptFrontendNode from loguru import logger @@ -21,7 +21,7 @@ class PromptCreator(LangChainTypeCreator): @property def type_to_loader_dict(self) -> Dict: - settings_manager = get_settings_manager() + settings_service = get_settings_service() if self.type_dict is None: self.type_dict = { prompt_name: import_class(f"langchain.prompts.{prompt_name}") @@ -36,8 +36,8 @@ class PromptCreator(LangChainTypeCreator): self.type_dict = { name: prompt for name, prompt in self.type_dict.items() - if name in settings_manager.settings.PROMPTS - or settings_manager.settings.DEV + if name in settings_service.settings.PROMPTS + or settings_service.settings.DEV } return self.type_dict diff --git a/src/backend/langflow/interface/retrievers/base.py b/src/backend/langflow/interface/retrievers/base.py index 92e3f2f61..5562cf264 100644 --- a/src/backend/langflow/interface/retrievers/base.py +++ b/src/backend/langflow/interface/retrievers/base.py @@ -4,7 +4,7 @@ from langchain import retrievers from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.retrievers import RetrieverFrontendNode from loguru import logger @@ -49,12 +49,12 @@ class RetrieverCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ retriever for retriever in self.type_to_loader_dict.keys() - if retriever in settings_manager.settings.RETRIEVERS - or settings_manager.settings.DEV + if retriever in settings_service.settings.RETRIEVERS + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/interface/text_splitters/base.py b/src/backend/langflow/interface/text_splitters/base.py index 8b21303ce..abf3640c8 100644 --- a/src/backend/langflow/interface/text_splitters/base.py +++ b/src/backend/langflow/interface/text_splitters/base.py @@ -1,7 +1,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode from langflow.interface.custom_lists import textsplitter_type_to_cls_dict @@ -31,12 +31,12 @@ class TextSplitterCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ textsplitter.__name__ for textsplitter in self.type_to_loader_dict.values() - if textsplitter.__name__ in settings_manager.settings.TEXTSPLITTERS - or settings_manager.settings.DEV + if textsplitter.__name__ in settings_service.settings.TEXTSPLITTERS + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/interface/toolkits/base.py b/src/backend/langflow/interface/toolkits/base.py index fe0003b15..d358fcde8 100644 --- a/src/backend/langflow/interface/toolkits/base.py +++ b/src/backend/langflow/interface/toolkits/base.py @@ -4,7 +4,7 @@ from langchain.agents import agent_toolkits from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class, import_module -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from loguru import logger from langflow.utils.util import build_template_from_class @@ -30,7 +30,7 @@ class ToolkitCreator(LangChainTypeCreator): @property def type_to_loader_dict(self) -> Dict: if self.type_dict is None: - settings_manager = get_settings_manager() + settings_service = get_settings_service() self.type_dict = { toolkit_name: import_class( f"langchain.agents.agent_toolkits.{toolkit_name}" @@ -38,7 +38,7 @@ class ToolkitCreator(LangChainTypeCreator): # if toolkit_name is not lower case it is a class for toolkit_name in agent_toolkits.__all__ if not toolkit_name.islower() - and toolkit_name in settings_manager.settings.TOOLKITS + and toolkit_name in settings_service.settings.TOOLKITS } return self.type_dict diff --git a/src/backend/langflow/interface/tools/base.py b/src/backend/langflow/interface/tools/base.py index 1dbc9a6ed..cdc161d8f 100644 --- a/src/backend/langflow/interface/tools/base.py +++ b/src/backend/langflow/interface/tools/base.py @@ -15,7 +15,7 @@ from langflow.interface.tools.constants import ( OTHER_TOOLS, ) from langflow.interface.tools.util import get_tool_params -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.field.base import TemplateField from langflow.template.template.base import Template @@ -67,7 +67,7 @@ class ToolCreator(LangChainTypeCreator): @property def type_to_loader_dict(self) -> Dict: - settings_manager = get_settings_manager() + settings_service = get_settings_service() if self.tools_dict is None: all_tools = {} @@ -77,8 +77,8 @@ class ToolCreator(LangChainTypeCreator): tool_name = tool_params.get("name") or tool if ( - tool_name in settings_manager.settings.TOOLS - or settings_manager.settings.DEV + tool_name in settings_service.settings.TOOLS + or settings_service.settings.DEV ): if tool_name == "JsonSpec": tool_params["path"] = tool_params.pop("dict_") # type: ignore diff --git a/src/backend/langflow/interface/utilities/base.py b/src/backend/langflow/interface/utilities/base.py index 9009983b0..b1e7c461b 100644 --- a/src/backend/langflow/interface/utilities/base.py +++ b/src/backend/langflow/interface/utilities/base.py @@ -5,7 +5,7 @@ from langchain import SQLDatabase, utilities from langflow.custom.customs import get_custom_nodes from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.utilities import UtilitiesFrontendNode from loguru import logger @@ -27,7 +27,7 @@ class UtilityCreator(LangChainTypeCreator): from the langchain.chains module and filtering them according to the settings.utilities list. """ if self.type_dict is None: - settings_manager = get_settings_manager() + settings_service = get_settings_service() self.type_dict = { utility_name: import_class(f"langchain.utilities.{utility_name}") for utility_name in utilities.__all__ @@ -37,8 +37,8 @@ class UtilityCreator(LangChainTypeCreator): self.type_dict = { name: utility for name, utility in self.type_dict.items() - if name in settings_manager.settings.UTILITIES - or settings_manager.settings.DEV + if name in settings_service.settings.UTILITIES + or settings_service.settings.DEV } return self.type_dict diff --git a/src/backend/langflow/interface/utils.py b/src/backend/langflow/interface/utils.py index c67cb5faa..26c28fe86 100644 --- a/src/backend/langflow/interface/utils.py +++ b/src/backend/langflow/interface/utils.py @@ -10,7 +10,7 @@ from langchain.base_language import BaseLanguageModel from PIL.Image import Image from loguru import logger from langflow.services.chat.config import ChatConfig -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service def load_file_into_dict(file_path: str) -> dict: @@ -64,11 +64,11 @@ def extract_input_variables_from_prompt(prompt: str) -> list[str]: def setup_llm_caching(): """Setup LLM caching.""" - settings_manager = get_settings_manager() + settings_service = get_settings_service() try: - set_langchain_cache(settings_manager.settings) + set_langchain_cache(settings_service.settings) except ImportError: - logger.warning(f"Could not import {settings_manager.settings.CACHE_TYPE}. ") + logger.warning(f"Could not import {settings_service.settings.CACHE_TYPE}. ") except Exception as exc: logger.warning(f"Could not setup LLM caching. Error: {exc}") diff --git a/src/backend/langflow/interface/vector_store/base.py b/src/backend/langflow/interface/vector_store/base.py index f7aca8c9c..7c0567362 100644 --- a/src/backend/langflow/interface/vector_store/base.py +++ b/src/backend/langflow/interface/vector_store/base.py @@ -4,7 +4,7 @@ from langchain import vectorstores from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode from loguru import logger @@ -44,12 +44,12 @@ class VectorstoreCreator(LangChainTypeCreator): return None def to_list(self) -> List[str]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() return [ vectorstore for vectorstore in self.type_to_loader_dict.keys() - if vectorstore in settings_manager.settings.VECTORSTORES - or settings_manager.settings.DEV + if vectorstore in settings_service.settings.VECTORSTORES + or settings_service.settings.DEV ] diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index fe180bb7d..877d32df2 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -7,7 +7,7 @@ from langflow.interface.run import ( get_memory_key, update_memory_keys, ) -from langflow.services.utils import get_session_manager +from langflow.services.utils import get_session_service from loguru import logger from langflow.graph import Graph from langchain.chains.base import Chain @@ -158,20 +158,20 @@ async def process_graph_cached( clear_cache=False, session_id=None, ) -> Result: - session_manager = get_session_manager() + session_service = get_session_service() if clear_cache: - session_manager.clear_session(session_id) + session_service.clear_session(session_id) if session_id is None: - session_id = session_manager.generate_key( + session_id = session_service.generate_key( session_id=session_id, data_graph=data_graph ) - # Load the graph using SessionManager - langchain_object, artifacts = session_manager.load_session(session_id, data_graph) + # Load the graph using SessionService + langchain_object, artifacts = session_service.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) result = generate_result(langchain_object, processed_inputs) # langchain_object is now updated with the new memory # we need to update the cache with the updated langchain_object - session_manager.update_session(session_id, (langchain_object, artifacts)) + session_service.update_session(session_id, (langchain_object, artifacts)) return Result(result, session_id) diff --git a/src/backend/langflow/services/__init__.py b/src/backend/langflow/services/__init__.py index 8ac74b5b9..c58f34b1d 100644 --- a/src/backend/langflow/services/__init__.py +++ b/src/backend/langflow/services/__init__.py @@ -1,4 +1,4 @@ -from .manager import service_manager +from .manager import service_service from .schema import ServiceType -__all__ = ["service_manager", "ServiceType"] +__all__ = ["service_service", "ServiceType"] diff --git a/src/backend/langflow/services/auth/factory.py b/src/backend/langflow/services/auth/factory.py index 4914ce645..b44019289 100644 --- a/src/backend/langflow/services/auth/factory.py +++ b/src/backend/langflow/services/auth/factory.py @@ -1,12 +1,12 @@ from langflow.services.factory import ServiceFactory -from langflow.services.auth.service import AuthManager +from langflow.services.auth.service import AuthService -class AuthManagerFactory(ServiceFactory): - name = "auth_manager" +class AuthServiceFactory(ServiceFactory): + name = "auth_service" def __init__(self): - super().__init__(AuthManager) + super().__init__(AuthService) - def create(self, settings_manager): - return AuthManager(settings_manager) + def create(self, settings_service): + return AuthService(settings_service) diff --git a/src/backend/langflow/services/auth/service.py b/src/backend/langflow/services/auth/service.py index 29984a75c..5b0acf8c6 100644 --- a/src/backend/langflow/services/auth/service.py +++ b/src/backend/langflow/services/auth/service.py @@ -2,11 +2,11 @@ from langflow.services.base import Service from typing import TYPE_CHECKING if TYPE_CHECKING: - from langflow.services.settings.manager import SettingsManager + from langflow.services.settings.manager import SettingsService -class AuthManager(Service): - name = "auth_manager" +class AuthService(Service): + name = "auth_service" - def __init__(self, settings_manager: "SettingsManager"): - self.settings_manager = settings_manager + def __init__(self, settings_service: "SettingsService"): + self.settings_service = settings_service diff --git a/src/backend/langflow/services/auth/utils.py b/src/backend/langflow/services/auth/utils.py index 7787684b6..3d52ba80f 100644 --- a/src/backend/langflow/services/auth/utils.py +++ b/src/backend/langflow/services/auth/utils.py @@ -12,7 +12,7 @@ from langflow.services.database.models.user.crud import ( get_user_by_username, update_user_last_login_at, ) -from langflow.services.utils import get_session, get_settings_manager +from langflow.services.utils import get_session, get_settings_service from sqlmodel import Session oauth2_login = OAuth2PasswordBearer(tokenUrl="api/v1/login") @@ -33,18 +33,18 @@ async def api_key_security( header_param: str = Security(api_key_header), db: Session = Depends(get_session), ) -> Optional[User]: - settings_manager = get_settings_manager() + settings_service = get_settings_service() result: Optional[Union[ApiKey, User]] = None - if settings_manager.auth_settings.AUTO_LOGIN: + if settings_service.auth_settings.AUTO_LOGIN: # Get the first user - if not settings_manager.auth_settings.FIRST_SUPERUSER: + if not settings_service.auth_settings.FIRST_SUPERUSER: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Missing first superuser credentials", ) result = get_user_by_username( - db, settings_manager.auth_settings.FIRST_SUPERUSER + db, settings_service.auth_settings.FIRST_SUPERUSER ) elif not query_param and not header_param: @@ -74,7 +74,7 @@ async def get_current_user( token: Annotated[str, Depends(oauth2_login)], db: Session = Depends(get_session), ) -> User: - settings_manager = get_settings_manager() + settings_service = get_settings_service() credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -85,14 +85,14 @@ async def get_current_user( if isinstance(token, Coroutine): token = await token - if settings_manager.auth_settings.SECRET_KEY is None: + if settings_service.auth_settings.SECRET_KEY is None: raise credentials_exception try: payload = jwt.decode( token, - settings_manager.auth_settings.SECRET_KEY, - algorithms=[settings_manager.auth_settings.ALGORITHM], + settings_service.auth_settings.SECRET_KEY, + algorithms=[settings_service.auth_settings.ALGORITHM], ) user_id: UUID = payload.get("sub") # type: ignore token_type: str = payload.get("type") # type: ignore @@ -132,19 +132,19 @@ def get_current_active_superuser( def verify_password(plain_password, hashed_password): - settings_manager = get_settings_manager() - return settings_manager.auth_settings.pwd_context.verify( + settings_service = get_settings_service() + return settings_service.auth_settings.pwd_context.verify( plain_password, hashed_password ) def get_password_hash(password): - settings_manager = get_settings_manager() - return settings_manager.auth_settings.pwd_context.hash(password) + settings_service = get_settings_service() + return settings_service.auth_settings.pwd_context.hash(password) def create_token(data: dict, expires_delta: timedelta): - settings_manager = get_settings_manager() + settings_service = get_settings_service() to_encode = data.copy() expire = datetime.now(timezone.utc) + expires_delta @@ -152,8 +152,8 @@ def create_token(data: dict, expires_delta: timedelta): return jwt.encode( to_encode, - settings_manager.auth_settings.SECRET_KEY, - algorithm=settings_manager.auth_settings.ALGORITHM, + settings_service.auth_settings.SECRET_KEY, + algorithm=settings_service.auth_settings.ALGORITHM, ) @@ -181,9 +181,9 @@ def create_super_user( def create_user_longterm_token(db: Session = Depends(get_session)) -> dict: - settings_manager = get_settings_manager() - username = settings_manager.auth_settings.FIRST_SUPERUSER - password = settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD + settings_service = get_settings_service() + username = settings_service.auth_settings.FIRST_SUPERUSER + password = settings_service.auth_settings.FIRST_SUPERUSER_PASSWORD if not username or not password: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, @@ -227,10 +227,10 @@ def get_user_id_from_token(token: str) -> UUID: def create_user_tokens( user_id: UUID, db: Session = Depends(get_session), update_last_login: bool = False ) -> dict: - settings_manager = get_settings_manager() + settings_service = get_settings_service() access_token_expires = timedelta( - minutes=settings_manager.auth_settings.ACCESS_TOKEN_EXPIRE_MINUTES + minutes=settings_service.auth_settings.ACCESS_TOKEN_EXPIRE_MINUTES ) access_token = create_token( data={"sub": str(user_id)}, @@ -238,7 +238,7 @@ def create_user_tokens( ) refresh_token_expires = timedelta( - minutes=settings_manager.auth_settings.REFRESH_TOKEN_EXPIRE_MINUTES + minutes=settings_service.auth_settings.REFRESH_TOKEN_EXPIRE_MINUTES ) refresh_token = create_token( data={"sub": str(user_id), "type": "rf"}, @@ -257,13 +257,13 @@ def create_user_tokens( def create_refresh_token(refresh_token: str, db: Session = Depends(get_session)): - settings_manager = get_settings_manager() + settings_service = get_settings_service() try: payload = jwt.decode( refresh_token, - settings_manager.auth_settings.SECRET_KEY, - algorithms=[settings_manager.auth_settings.ALGORITHM], + settings_service.auth_settings.SECRET_KEY, + algorithms=[settings_service.auth_settings.ALGORITHM], ) user_id: UUID = payload.get("sub") # type: ignore token_type: str = payload.get("type") # type: ignore diff --git a/src/backend/langflow/services/cache/base.py b/src/backend/langflow/services/cache/base.py index 531f31284..4eee6639e 100644 --- a/src/backend/langflow/services/cache/base.py +++ b/src/backend/langflow/services/cache/base.py @@ -1,12 +1,12 @@ import abc -class BaseCacheManager(abc.ABC): +class BaseCacheService(abc.ABC): """ Abstract base class for a cache. """ - name = "cache_manager" + name = "cache_service" @abc.abstractmethod def get(self, key): diff --git a/src/backend/langflow/services/cache/factory.py b/src/backend/langflow/services/cache/factory.py index 4e497c5fd..f00ab239f 100644 --- a/src/backend/langflow/services/cache/factory.py +++ b/src/backend/langflow/services/cache/factory.py @@ -1,27 +1,27 @@ -from langflow.services.cache.manager import InMemoryCache, RedisCache, BaseCacheManager +from langflow.services.cache.manager import InMemoryCache, RedisCache, BaseCacheService from langflow.services.factory import ServiceFactory from langflow.utils.logger import logger from typing import TYPE_CHECKING if TYPE_CHECKING: - from langflow.services.settings.manager import SettingsManager + from langflow.services.settings.manager import SettingsService -class CacheManagerFactory(ServiceFactory): +class CacheServiceFactory(ServiceFactory): def __init__(self): - super().__init__(BaseCacheManager) + super().__init__(BaseCacheService) - def create(self, settings_manager: "SettingsManager"): - # Here you would have logic to create and configure a CacheManager + def create(self, settings_service: "SettingsService"): + # Here you would have logic to create and configure a CacheService # based on the settings_service - if settings_manager.settings.CACHE_TYPE == "redis": + if settings_service.settings.CACHE_TYPE == "redis": logger.debug("Creating Redis cache") redis_cache = RedisCache( - host=settings_manager.settings.REDIS_HOST, - port=settings_manager.settings.REDIS_PORT, - db=settings_manager.settings.REDIS_DB, - expiration_time=settings_manager.settings.REDIS_CACHE_EXPIRE, + host=settings_service.settings.REDIS_HOST, + port=settings_service.settings.REDIS_PORT, + db=settings_service.settings.REDIS_DB, + expiration_time=settings_service.settings.REDIS_CACHE_EXPIRE, ) if redis_cache.is_connected(): logger.debug("Redis cache is connected") @@ -31,5 +31,5 @@ class CacheManagerFactory(ServiceFactory): ) return InMemoryCache() - elif settings_manager.settings.CACHE_TYPE == "memory": + elif settings_service.settings.CACHE_TYPE == "memory": return InMemoryCache() diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index 47cb82ecf..9042757ae 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -3,12 +3,12 @@ import time from collections import OrderedDict from langflow.services.base import Service -from langflow.services.cache.base import BaseCacheManager +from langflow.services.cache.base import BaseCacheService import pickle -class InMemoryCache(BaseCacheManager, Service): +class InMemoryCache(BaseCacheService, Service): """ A simple in-memory cache using an OrderedDict. @@ -176,7 +176,7 @@ class InMemoryCache(BaseCacheManager, Service): return f"InMemoryCache(max_size={self.max_size}, expiration_time={self.expiration_time})" -class RedisCache(BaseCacheManager, Service): +class RedisCache(BaseCacheService, Service): """ A Redis-based cache implementation. diff --git a/src/backend/langflow/services/chat/cache.py b/src/backend/langflow/services/chat/cache.py index ce9a338ef..f6247b540 100644 --- a/src/backend/langflow/services/chat/cache.py +++ b/src/backend/langflow/services/chat/cache.py @@ -50,10 +50,10 @@ class AsyncSubject: await observer() -class CacheManager(Subject, Service): +class CacheService(Subject, Service): """Manages cache for different clients and notifies observers on changes.""" - name = "cache_manager" + name = "cache_service" def __init__(self): super().__init__() @@ -150,4 +150,4 @@ class CacheManager(Subject, Service): return list(self.current_cache.values())[-1] -cache_manager = CacheManager() +cache_service = CacheService() diff --git a/src/backend/langflow/services/chat/factory.py b/src/backend/langflow/services/chat/factory.py index ca844893a..54af7fcca 100644 --- a/src/backend/langflow/services/chat/factory.py +++ b/src/backend/langflow/services/chat/factory.py @@ -1,11 +1,11 @@ -from langflow.services.chat.manager import ChatManager +from langflow.services.chat.manager import ChatService from langflow.services.factory import ServiceFactory -class ChatManagerFactory(ServiceFactory): +class ChatServiceFactory(ServiceFactory): def __init__(self): - super().__init__(ChatManager) + super().__init__(ChatService) def create(self): - # Here you would have logic to create and configure a ChatManager - return ChatManager() + # Here you would have logic to create and configure a ChatService + return ChatService() diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index 5bd46b32d..bb86adfbd 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -7,11 +7,11 @@ from langflow.services.chat.cache import Subject from langflow.services.chat.utils import process_graph from loguru import logger -from .cache import cache_manager +from .cache import cache_service import asyncio from typing import Any, Dict, List -from langflow.services import service_manager, ServiceType +from langflow.services import service_service, ServiceType import orjson @@ -42,15 +42,15 @@ class ChatHistory(Subject): self.history[client_id] = [] -class ChatManager(Service): - name = "chat_manager" +class ChatService(Service): + name = "chat_service" def __init__(self): self.active_connections: Dict[str, WebSocket] = {} self.chat_history = ChatHistory() - self.chat_cache = cache_manager + self.chat_cache = cache_service self.chat_cache.attach(self.update) - self.cache_manager = service_manager.get(ServiceType.CACHE_MANAGER) + self.cache_service = service_service.get(ServiceType.CACHE_MANAGER) def on_chat_history_update(self): """Send the last chat message to the client.""" @@ -179,8 +179,8 @@ class ChatManager(Service): "result": langchain_object, "type": type(langchain_object), } - self.cache_manager.upsert(client_id, result_dict) - return client_id in self.cache_manager + self.cache_service.upsert(client_id, result_dict) + return client_id in self.cache_service async def handle_websocket(self, client_id: str, websocket: WebSocket): await self.connect(client_id, websocket) @@ -202,7 +202,7 @@ class ChatManager(Service): continue with self.chat_cache.set_client_id(client_id): - if langchain_object := self.cache_manager.get(client_id).get( + if langchain_object := self.cache_service.get(client_id).get( "result" ): await self.process_message(client_id, payload, langchain_object) diff --git a/src/backend/langflow/services/database/factory.py b/src/backend/langflow/services/database/factory.py index 25427b7b9..3726f520b 100644 --- a/src/backend/langflow/services/database/factory.py +++ b/src/backend/langflow/services/database/factory.py @@ -1,17 +1,17 @@ from typing import TYPE_CHECKING -from langflow.services.database.manager import DatabaseManager +from langflow.services.database.manager import DatabaseService from langflow.services.factory import ServiceFactory if TYPE_CHECKING: - from langflow.services.settings.manager import SettingsManager + from langflow.services.settings.manager import SettingsService -class DatabaseManagerFactory(ServiceFactory): +class DatabaseServiceFactory(ServiceFactory): def __init__(self): - super().__init__(DatabaseManager) + super().__init__(DatabaseService) - def create(self, settings_manager: "SettingsManager"): - # Here you would have logic to create and configure a DatabaseManager - if not settings_manager.settings.DATABASE_URL: + def create(self, settings_service: "SettingsService"): + # Here you would have logic to create and configure a DatabaseService + if not settings_service.settings.DATABASE_URL: raise ValueError("No database URL provided") - return DatabaseManager(settings_manager.settings.DATABASE_URL) + return DatabaseService(settings_service.settings.DATABASE_URL) diff --git a/src/backend/langflow/services/database/manager.py b/src/backend/langflow/services/database/manager.py index a89db8395..f348a6a5d 100644 --- a/src/backend/langflow/services/database/manager.py +++ b/src/backend/langflow/services/database/manager.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING from langflow.services.base import Service from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.utils import Result, TableResults -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from sqlalchemy import inspect import sqlalchemy as sa from sqlmodel import SQLModel, Session, create_engine @@ -16,8 +16,8 @@ if TYPE_CHECKING: from sqlalchemy.engine import Engine -class DatabaseManager(Service): - name = "database_manager" +class DatabaseService(Service): + name = "database_service" def __init__(self, database_url: str): self.database_url = database_url @@ -30,10 +30,10 @@ class DatabaseManager(Service): def _create_engine(self) -> "Engine": """Create the engine for the database.""" - settings_manager = get_settings_manager() + settings_service = get_settings_service() if ( - settings_manager.settings.DATABASE_URL - and settings_manager.settings.DATABASE_URL.startswith("sqlite") + settings_service.settings.DATABASE_URL + and settings_service.settings.DATABASE_URL.startswith("sqlite") ): connect_args = {"check_same_thread": False} else: @@ -162,12 +162,12 @@ class DatabaseManager(Service): def teardown(self): logger.debug("Tearing down database") try: - settings_manager = get_settings_manager() + settings_service = get_settings_service() # remove the default superuser if auto_login is enabled # using the FIRST_SUPERUSER to get the user - if settings_manager.auth_settings.AUTO_LOGIN: + if settings_service.auth_settings.AUTO_LOGIN: logger.debug("Removing default superuser") - username = settings_manager.auth_settings.FIRST_SUPERUSER + username = settings_service.auth_settings.FIRST_SUPERUSER with Session(self.engine) as session: user = get_user_by_username(session, username) session.delete(user) diff --git a/src/backend/langflow/services/database/utils.py b/src/backend/langflow/services/database/utils.py index fd0a8856a..db4fdf124 100644 --- a/src/backend/langflow/services/database/utils.py +++ b/src/backend/langflow/services/database/utils.py @@ -6,21 +6,21 @@ from alembic.util.exc import CommandError from sqlmodel import Session if TYPE_CHECKING: - from langflow.services.database.manager import DatabaseManager + from langflow.services.database.manager import DatabaseService def initialize_database(): logger.debug("Initializing database") - from langflow.services import service_manager, ServiceType + from langflow.services import service_service, ServiceType - database_manager = service_manager.get(ServiceType.DATABASE_MANAGER) + database_service = service_service.get(ServiceType.DATABASE_MANAGER) try: - database_manager.check_schema_health() + database_service.check_schema_health() except Exception as exc: logger.error(f"Error checking schema health: {exc}") raise RuntimeError("Error checking schema health") from exc try: - database_manager.run_migrations() + database_service.run_migrations() except CommandError as exc: if "Can't locate revision identified by" not in str(exc): raise exc @@ -30,23 +30,23 @@ def initialize_database(): logger.warning( "Wrong revision in DB, deleting alembic_version table and running migrations again" ) - with session_getter(database_manager) as session: + with session_getter(database_service) as session: session.execute("DROP TABLE alembic_version") - database_manager.run_migrations() + database_service.run_migrations() except Exception as exc: # if the exception involves tables already existing # we can ignore it if "already exists" not in str(exc): logger.error(f"Error running migrations: {exc}") raise RuntimeError("Error running migrations") from exc - database_manager.create_db_and_tables() + database_service.create_db_and_tables() logger.debug("Database initialized") @contextmanager -def session_getter(db_manager: "DatabaseManager"): +def session_getter(db_service: "DatabaseService"): try: - session = Session(db_manager.engine) + session = Session(db_service.engine) yield session except Exception as e: print("Session rollback because of exception:", e) diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index dc901730c..d7215a4cc 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from langflow.services.base import Service -class ServiceManager: +class ServiceService: """ Manages the creation of different services. """ @@ -95,7 +95,7 @@ class ServiceManager: self.dependencies = {} -service_manager = ServiceManager() +service_service = ServiceService() def initialize_services(): @@ -106,67 +106,67 @@ def initialize_services(): from langflow.services.cache import factory as cache_factory from langflow.services.chat import factory as chat_factory from langflow.services.settings import factory as settings_factory - from langflow.services.session import factory as session_manager_factory + from langflow.services.session import factory as session_service_factory from langflow.services.auth import factory as auth_factory from langflow.services.task import factory as task_factory - service_manager.register_factory(settings_factory.SettingsManagerFactory()) - service_manager.register_factory( - database_factory.DatabaseManagerFactory(), + service_service.register_factory(settings_factory.SettingsServiceFactory()) + service_service.register_factory( + database_factory.DatabaseServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER], ) - service_manager.register_factory( - cache_factory.CacheManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + service_service.register_factory( + cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] ) - service_manager.register_factory( - auth_factory.AuthManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + service_service.register_factory( + auth_factory.AuthServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] ) - service_manager.register_factory(chat_factory.ChatManagerFactory()) - service_manager.register_factory( - session_manager_factory.SessionManagerFactory(), + service_service.register_factory(chat_factory.ChatServiceFactory()) + service_service.register_factory( + session_service_factory.SessionServiceFactory(), dependencies=[ServiceType.CACHE_MANAGER], ) - service_manager.register_factory( - task_factory.TaskManagerFactory(), + service_service.register_factory( + task_factory.TaskServiceFactory(), ) # Test cache connection - service_manager.get(ServiceType.CACHE_MANAGER) + service_service.get(ServiceType.CACHE_MANAGER) # Test database connection - service_manager.get(ServiceType.DATABASE_MANAGER) + service_service.get(ServiceType.DATABASE_MANAGER) # Test cache connection - service_manager.get(ServiceType.CACHE_MANAGER) + service_service.get(ServiceType.CACHE_MANAGER) # Test database connection - service_manager.get(ServiceType.DATABASE_MANAGER) + service_service.get(ServiceType.DATABASE_MANAGER) -def initialize_settings_manager(): +def initialize_settings_service(): """ Initialize the settings manager. """ from langflow.services.settings import factory as settings_factory - service_manager.register_factory(settings_factory.SettingsManagerFactory()) + service_service.register_factory(settings_factory.SettingsServiceFactory()) -def initialize_session_manager(): +def initialize_session_service(): """ Initialize the session manager. """ - from langflow.services.session import factory as session_manager_factory # type: ignore + from langflow.services.session import factory as session_service_factory # type: ignore from langflow.services.cache import factory as cache_factory - initialize_settings_manager() + initialize_settings_service() - service_manager.register_factory( - cache_factory.CacheManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + service_service.register_factory( + cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] ) - service_manager.register_factory( - session_manager_factory.SessionManagerFactory(), + service_service.register_factory( + session_service_factory.SessionServiceFactory(), dependencies=[ServiceType.CACHE_MANAGER], ) @@ -175,4 +175,4 @@ def teardown_services(): """ Teardown all the services. """ - service_manager.teardown() + service_service.teardown() diff --git a/src/backend/langflow/services/schema.py b/src/backend/langflow/services/schema.py index 801e0cacd..8b2bb75c2 100644 --- a/src/backend/langflow/services/schema.py +++ b/src/backend/langflow/services/schema.py @@ -7,10 +7,10 @@ class ServiceType(str, Enum): registered with the service manager. """ - AUTH_MANAGER = "auth_manager" - CACHE_MANAGER = "cache_manager" - SETTINGS_MANAGER = "settings_manager" - DATABASE_MANAGER = "database_manager" - CHAT_MANAGER = "chat_manager" - SESSION_MANAGER = "session_manager" - TASK_MANAGER = "task_manager" + AUTH_MANAGER = "auth_service" + CACHE_MANAGER = "cache_service" + SETTINGS_MANAGER = "settings_service" + DATABASE_MANAGER = "database_service" + CHAT_MANAGER = "chat_service" + SESSION_MANAGER = "session_service" + TASK_MANAGER = "task_service" diff --git a/src/backend/langflow/services/session/factory.py b/src/backend/langflow/services/session/factory.py index 42b33e70f..9abe025a8 100644 --- a/src/backend/langflow/services/session/factory.py +++ b/src/backend/langflow/services/session/factory.py @@ -1,14 +1,14 @@ from typing import TYPE_CHECKING -from langflow.services.session.manager import SessionManager +from langflow.services.session.manager import SessionService from langflow.services.factory import ServiceFactory if TYPE_CHECKING: - from langflow.services.cache.manager import BaseCacheManager + from langflow.services.cache.manager import BaseCacheService -class SessionManagerFactory(ServiceFactory): +class SessionServiceFactory(ServiceFactory): def __init__(self): - super().__init__(SessionManager) + super().__init__(SessionService) - def create(self, cache_manager: "BaseCacheManager"): - return SessionManager(cache_manager) + def create(self, cache_service: "BaseCacheService"): + return SessionService(cache_service) diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index de9fa2c5d..44108b14f 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -6,23 +6,23 @@ from langflow.services.cache.utils import compute_dict_hash from langflow.services.session.utils import session_id_generator if TYPE_CHECKING: - from langflow.services.cache.base import BaseCacheManager + from langflow.services.cache.base import BaseCacheService -class SessionManager(Service): - name = "session_manager" +class SessionService(Service): + name = "session_service" - def __init__(self, cache_manager): - self.cache_manager: "BaseCacheManager" = cache_manager + def __init__(self, cache_service): + self.cache_service: "BaseCacheService" = cache_service def load_session(self, key, data_graph): # Check if the data is cached - if key in self.cache_manager: - return self.cache_manager.get(key) + if key in self.cache_service: + return self.cache_service.get(key) # If not cached, build the graph and cache it graph, artifacts = build_sorted_vertices(data_graph) - self.cache_manager.set(key, (graph, artifacts)) + self.cache_service.set(key, (graph, artifacts)) return graph, artifacts @@ -38,7 +38,7 @@ class SessionManager(Service): return self.build_key(session_id, data_graph=data_graph) def update_session(self, session_id, value): - self.cache_manager.set(session_id, value) + self.cache_service.set(session_id, value) def clear_session(self, session_id): - self.cache_manager.delete(session_id) + self.cache_service.delete(session_id) diff --git a/src/backend/langflow/services/settings/factory.py b/src/backend/langflow/services/settings/factory.py index ab22e22b8..9202ae8c3 100644 --- a/src/backend/langflow/services/settings/factory.py +++ b/src/backend/langflow/services/settings/factory.py @@ -1,15 +1,15 @@ from pathlib import Path -from langflow.services.settings.manager import SettingsManager +from langflow.services.settings.manager import SettingsService from langflow.services.factory import ServiceFactory -class SettingsManagerFactory(ServiceFactory): +class SettingsServiceFactory(ServiceFactory): def __init__(self): - super().__init__(SettingsManager) + super().__init__(SettingsService) def create(self): - # Here you would have logic to create and configure a SettingsManager + # Here you would have logic to create and configure a SettingsService langflow_dir = Path(__file__).parent.parent.parent - return SettingsManager.load_settings_from_yaml( + return SettingsService.load_settings_from_yaml( str(langflow_dir / "config.yaml") ) diff --git a/src/backend/langflow/services/settings/manager.py b/src/backend/langflow/services/settings/manager.py index 2d687d784..cdededcea 100644 --- a/src/backend/langflow/services/settings/manager.py +++ b/src/backend/langflow/services/settings/manager.py @@ -6,8 +6,8 @@ import os import yaml -class SettingsManager(Service): - name = "settings_manager" +class SettingsService(Service): + name = "settings_service" def __init__(self, settings: Settings, auth_settings: AuthSettings): super().__init__() @@ -15,7 +15,7 @@ class SettingsManager(Service): self.auth_settings = auth_settings @classmethod - def load_settings_from_yaml(cls, file_path: str) -> "SettingsManager": + def load_settings_from_yaml(cls, file_path: str) -> "SettingsService": # Check if a string is a valid path or a file name if "/" not in file_path: # Get current path diff --git a/src/backend/langflow/services/task/factory.py b/src/backend/langflow/services/task/factory.py index b21cb38cc..efb6ac24d 100644 --- a/src/backend/langflow/services/task/factory.py +++ b/src/backend/langflow/services/task/factory.py @@ -1,11 +1,11 @@ -from langflow.services.task.manager import TaskManager +from langflow.services.task.manager import TaskService from langflow.services.factory import ServiceFactory -class TaskManagerFactory(ServiceFactory): +class TaskServiceFactory(ServiceFactory): def __init__(self): - super().__init__(TaskManager) + super().__init__(TaskService) def create(self): - # Here you would have logic to create and configure a TaskManager - return TaskManager() + # Here you would have logic to create and configure a TaskService + return TaskService() diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py index cdf731917..e0448ab66 100644 --- a/src/backend/langflow/services/task/manager.py +++ b/src/backend/langflow/services/task/manager.py @@ -19,8 +19,8 @@ except ImportError: USE_CELERY = False -class TaskManager(Service): - name = "task_manager" +class TaskService(Service): + name = "task_service" def __init__(self): self.backend = self.get_backend() @@ -33,7 +33,7 @@ class TaskManager(Service): return CeleryBackend() return AnyIOBackend() - # In your TaskManager class + # In your TaskService class async def launch_and_await_task( self, task_func: Callable[..., Any], diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index d3e40981c..aea973e72 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -1,41 +1,41 @@ -from langflow.services import ServiceType, service_manager +from langflow.services import ServiceType, service_service from typing import TYPE_CHECKING, Generator if TYPE_CHECKING: - from langflow.services.database.manager import DatabaseManager - from langflow.services.settings.manager import SettingsManager - from langflow.services.cache.manager import BaseCacheManager - from langflow.services.session.manager import SessionManager - from langflow.services.task.manager import TaskManager - from langflow.services.chat.manager import ChatManager + from langflow.services.database.manager import DatabaseService + from langflow.services.settings.manager import SettingsService + from langflow.services.cache.manager import BaseCacheService + from langflow.services.session.manager import SessionService + from langflow.services.task.manager import TaskService + from langflow.services.chat.manager import ChatService from sqlmodel import Session -def get_settings_manager() -> "SettingsManager": - return service_manager.get(ServiceType.SETTINGS_MANAGER) +def get_settings_service() -> "SettingsService": + return service_service.get(ServiceType.SETTINGS_MANAGER) -def get_db_manager() -> "DatabaseManager": - return service_manager.get(ServiceType.DATABASE_MANAGER) +def get_db_service() -> "DatabaseService": + return service_service.get(ServiceType.DATABASE_MANAGER) def get_session() -> Generator["Session", None, None]: - db_manager = service_manager.get(ServiceType.DATABASE_MANAGER) - yield from db_manager.get_session() + db_service = service_service.get(ServiceType.DATABASE_MANAGER) + yield from db_service.get_session() -def get_cache_manager() -> "BaseCacheManager": - return service_manager.get(ServiceType.CACHE_MANAGER) +def get_cache_service() -> "BaseCacheService": + return service_service.get(ServiceType.CACHE_MANAGER) -def get_session_manager() -> "SessionManager": - return service_manager.get(ServiceType.SESSION_MANAGER) +def get_session_service() -> "SessionService": + return service_service.get(ServiceType.SESSION_MANAGER) -def get_task_manager() -> "TaskManager": - return service_manager.get(ServiceType.TASK_MANAGER) +def get_task_service() -> "TaskService": + return service_service.get(ServiceType.TASK_MANAGER) -def get_chat_manager() -> "ChatManager": - return service_manager.get(ServiceType.CHAT_MANAGER) +def get_chat_service() -> "ChatService": + return service_service.get(ServiceType.CHAT_MANAGER) diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index 2c8f92ee1..61b4a93aa 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -30,7 +30,7 @@ def build_template_from_function( variables = {"_type": _type} for class_field_items, value in _class.__fields__.items(): - if class_field_items in ["callback_manager"]: + if class_field_items in ["callback_service"]: continue variables[class_field_items] = {} for name_, value_ in value.__repr_args__(): @@ -84,7 +84,7 @@ def build_template_from_class( if "__fields__" in _class.__dict__: for class_field_items, value in _class.__fields__.items(): - if class_field_items in ["callback_manager"]: + if class_field_items in ["callback_service"]: continue variables[class_field_items] = {} for name_, value_ in value.__repr_args__(): diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 2077bc3ef..ad847a01c 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -6,8 +6,8 @@ from langflow.processing.process import ( generate_result, process_inputs, ) -from langflow.services.manager import initialize_session_manager -from langflow.services.utils import get_session_manager +from langflow.services.manager import initialize_session_service +from langflow.services.utils import get_session_service if TYPE_CHECKING: from langflow.graph.vertex.base import Vertex @@ -40,16 +40,16 @@ def process_graph_cached_task( clear_cache=False, session_id=None, ) -> Tuple[Any, str]: - initialize_session_manager() - session_manager = get_session_manager() + initialize_session_service() + session_service = get_session_service() if clear_cache: - session_manager.clear_session(session_id) - # Load the graph using SessionManager - langchain_object, artifacts = session_manager.load_session(session_id, data_graph) + session_service.clear_session(session_id) + # Load the graph using SessionService + langchain_object, artifacts = session_service.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) result = generate_result(langchain_object, processed_inputs) # langchain_object is now updated with the new memory # we need to update the cache with the updated langchain_object - session_manager.update_session(session_id, (langchain_object, artifacts)) + session_service.update_session(session_id, (langchain_object, artifacts)) return result, session_id diff --git a/tests/conftest.py b/tests/conftest.py index 8df9e1655..6730ac405 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,7 +17,7 @@ from sqlmodel.pool import StaticPool from typer.testing import CliRunner if TYPE_CHECKING: - from langflow.services.database.manager import DatabaseManager + from langflow.services.database.manager import DatabaseService def pytest_configure(): @@ -158,8 +158,8 @@ def session_getter_fixture(client): SQLModel.metadata.create_all(engine) @contextmanager - def blank_session_getter(db_manager: "DatabaseManager"): - with Session(db_manager.engine) as session: + def blank_session_getter(db_service: "DatabaseService"): + with Session(db_service.engine) as session: yield session yield blank_session_getter diff --git a/tests/test_agents_template.py b/tests/test_agents_template.py index b12ad7dee..85f689eda 100644 --- a/tests/test_agents_template.py +++ b/tests/test_agents_template.py @@ -31,15 +31,15 @@ def test_zero_shot_agent(client: TestClient, logged_in_headers): } # Additional assertions for other template variables - assert template["callback_manager"] == { + assert template["callback_service"] == { "required": False, "dynamic": False, "placeholder": "", "show": False, "multiline": False, "password": False, - "name": "callback_manager", - "type": "BaseCallbackManager", + "name": "callback_service", + "type": "BaseCallbackService", "list": False, "advanced": False, "info": "", diff --git a/tests/test_cache_manager.py b/tests/test_cache_manager.py index a560aff88..bbc94d9fb 100644 --- a/tests/test_cache_manager.py +++ b/tests/test_cache_manager.py @@ -2,81 +2,81 @@ from io import StringIO import pandas as pd import pytest -from langflow.services.chat.cache import CacheManager +from langflow.services.chat.cache import CacheService from PIL import Image @pytest.fixture -def cache_manager(): - return CacheManager() +def cache_service(): + return CacheService() -def test_cache_manager_attach_detach_notify(cache_manager): +def test_cache_service_attach_detach_notify(cache_service): observer_called = False def observer(): nonlocal observer_called observer_called = True - cache_manager.attach(observer) - cache_manager.notify() + cache_service.attach(observer) + cache_service.notify() assert observer_called observer_called = False - cache_manager.detach(observer) - cache_manager.notify() + cache_service.detach(observer) + cache_service.notify() assert not observer_called -def test_cache_manager_client_context(cache_manager): - with cache_manager.set_client_id("client1"): - cache_manager.add("foo", "bar", "string") - assert cache_manager.get("foo") == { +def test_cache_service_client_context(cache_service): + with cache_service.set_client_id("client1"): + cache_service.add("foo", "bar", "string") + assert cache_service.get("foo") == { "obj": "bar", "type": "string", "extension": "str", } - with cache_manager.set_client_id("client2"): - cache_manager.add("baz", "qux", "string") - assert cache_manager.get("baz") == { + with cache_service.set_client_id("client2"): + cache_service.add("baz", "qux", "string") + assert cache_service.get("baz") == { "obj": "qux", "type": "string", "extension": "str", } with pytest.raises(KeyError): - cache_manager.get("foo") + cache_service.get("foo") -def test_cache_manager_add_pandas(cache_manager): +def test_cache_service_add_pandas(cache_service): df = pd.DataFrame({"col1": [1, 2], "col2": [3, 4]}) - with cache_manager.set_client_id("client1"): - cache_manager.add_pandas("test_df", df) - cached_df = cache_manager.get("test_df") + with cache_service.set_client_id("client1"): + cache_service.add_pandas("test_df", df) + cached_df = cache_service.get("test_df") assert cached_df["type"] == "pandas" assert cached_df["extension"] == "csv" read_df = pd.read_csv(StringIO(cached_df["obj"]), index_col=0) pd.testing.assert_frame_equal(df, read_df) -def test_cache_manager_add_image(cache_manager): +def test_cache_service_add_image(cache_service): img = Image.new("RGB", (50, 50), color="red") - with cache_manager.set_client_id("client1"): - cache_manager.add_image("test_image", img) - cached_img = cache_manager.get("test_image") + with cache_service.set_client_id("client1"): + cache_service.add_image("test_image", img) + cached_img = cache_service.get("test_image") assert cached_img["type"] == "image" assert cached_img["extension"] == "png" assert isinstance(cached_img["obj"], Image.Image) -def test_cache_manager_get_last(cache_manager): - with cache_manager.set_client_id("client1"): - cache_manager.add("foo", "bar", "string") - cache_manager.add("baz", "qux", "string") - last_item = cache_manager.get_last() +def test_cache_service_get_last(cache_service): + with cache_service.set_client_id("client1"): + cache_service.add("foo", "bar", "string") + cache_service.add("baz", "qux", "string") + last_item = cache_service.get_last() assert last_item == {"obj": "qux", "type": "string", "extension": "str"} diff --git a/tests/test_cli.py b/tests/test_cli.py index 4ed00893e..dfedb9710 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -26,8 +26,8 @@ def test_components_path(runner, client, default_settings): ["run", "--components-path", str(temp_dir), *default_settings], ) assert result.exit_code == 0, result.stdout - settings_manager = utils.get_settings_manager() - assert str(temp_dir) in settings_manager.settings.COMPONENTS_PATH + settings_service = utils.get_settings_service() + assert str(temp_dir) in settings_service.settings.COMPONENTS_PATH def test_superuser(runner, client, session): diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index fac7adb34..3344633ab 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -2,7 +2,7 @@ import uuid from langflow.processing.process import Result from langflow.services.auth.utils import get_password_hash from langflow.services.database.models.api_key.api_key import ApiKey -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service import pytest from fastapi.testclient import TestClient from langflow.interface.tools.constants import CUSTOM_TOOLS @@ -111,8 +111,8 @@ def test_process_flow_invalid_api_key(client, flow, monkeypatch): from langflow.api.v1 import endpoints from langflow.services.database.models.api_key import crud - settings_manager = get_settings_manager() - settings_manager.auth_settings.AUTO_LOGIN = False + settings_service = get_settings_service() + settings_service.auth_settings.AUTO_LOGIN = False async def mock_process_graph_cached(*args, **kwargs): return Result(result={}, session_id="session_id_mock") @@ -170,8 +170,8 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k from langflow.api.v1 import endpoints from langflow.services.database.models.api_key import crud - settings_manager = get_settings_manager() - settings_manager.auth_settings.AUTO_LOGIN = False + settings_service = get_settings_service() + settings_service.auth_settings.AUTO_LOGIN = False async def mock_process_graph_cached(*args, **kwargs): return Result(result={}, session_id="session_id_mock") @@ -208,8 +208,8 @@ def test_process_flow_fails_autologin_off(client, flow, monkeypatch): from langflow.api.v1 import endpoints from langflow.services.database.models.api_key import crud - settings_manager = get_settings_manager() - settings_manager.auth_settings.AUTO_LOGIN = False + settings_service = get_settings_service() + settings_service.auth_settings.AUTO_LOGIN = False async def mock_process_graph_cached(*args, **kwargs): return Result(result={}, session_id="session_id_mock") diff --git a/tests/test_llms_template.py b/tests/test_llms_template.py index 14e151479..8be3e333b 100644 --- a/tests/test_llms_template.py +++ b/tests/test_llms_template.py @@ -1,14 +1,14 @@ from fastapi.testclient import TestClient -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service def test_llms_settings(client: TestClient, logged_in_headers): - settings_manager = get_settings_manager() + settings_service = get_settings_service() response = client.get("api/v1/all", headers=logged_in_headers) assert response.status_code == 200 json_response = response.json() llms = json_response["llms"] - assert set(llms.keys()) == set(settings_manager.settings.LLMS) + assert set(llms.keys()) == set(settings_service.settings.LLMS) # def test_hugging_face_hub(client: TestClient): diff --git a/tests/test_process.py b/tests/test_process.py index 0628676ee..bb0147616 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -1,5 +1,5 @@ from langflow.processing.process import process_tweaks -from langflow.services.utils import get_session_manager +from langflow.services.utils import get_session_service def test_no_tweaks(): @@ -199,13 +199,13 @@ def test_tweak_not_in_template(): def test_load_langchain_object_with_cached_session(client, basic_graph_data): # Provide a non-existent session_id - session_manager = get_session_manager() + session_service = get_session_service() session_id1 = "non-existent-session-id" - langchain_object1, artifacts1 = session_manager.load_session( + langchain_object1, artifacts1 = session_service.load_session( session_id1, basic_graph_data ) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2 = session_manager.load_session( + langchain_object2, artifacts2 = session_service.load_session( session_id1, basic_graph_data ) @@ -215,16 +215,16 @@ def test_load_langchain_object_with_cached_session(client, basic_graph_data): def test_load_langchain_object_with_no_cached_session(client, basic_graph_data): # Provide a non-existent session_id - session_manager = get_session_manager() + session_service = get_session_service() session_id1 = "non-existent-session-id" - session_id = session_manager.build_key(session_id1, basic_graph_data) - langchain_object1, artifacts1 = session_manager.load_session( + session_id = session_service.build_key(session_id1, basic_graph_data) + langchain_object1, artifacts1 = session_service.load_session( session_id, basic_graph_data ) # Clear the cache - session_manager.clear_session(session_id) + session_service.clear_session(session_id) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2 = session_manager.load_session( + langchain_object2, artifacts2 = session_service.load_session( session_id, basic_graph_data ) @@ -235,13 +235,13 @@ def test_load_langchain_object_with_no_cached_session(client, basic_graph_data): def test_load_langchain_object_without_session_id(client, basic_graph_data): # Provide a non-existent session_id - session_manager = get_session_manager() + session_service = get_session_service() session_id1 = None - langchain_object1, artifacts1 = session_manager.load_session( + langchain_object1, artifacts1 = session_service.load_session( session_id1, basic_graph_data ) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2 = session_manager.load_session( + langchain_object2, artifacts2 = session_service.load_session( session_id1, basic_graph_data ) diff --git a/tests/test_prompts_template.py b/tests/test_prompts_template.py index 676448f73..1ee2cd1e0 100644 --- a/tests/test_prompts_template.py +++ b/tests/test_prompts_template.py @@ -1,14 +1,14 @@ from fastapi.testclient import TestClient -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service def test_prompts_settings(client: TestClient, logged_in_headers): - settings_manager = get_settings_manager() + settings_service = get_settings_service() response = client.get("api/v1/all", headers=logged_in_headers) assert response.status_code == 200 json_response = response.json() prompts = json_response["prompts"] - assert set(prompts.keys()) == set(settings_manager.settings.PROMPTS) + assert set(prompts.keys()) == set(settings_service.settings.PROMPTS) def test_prompt_template(client: TestClient, logged_in_headers): diff --git a/tests/test_user.py b/tests/test_user.py index bc617e127..9c4153316 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -2,15 +2,15 @@ from datetime import datetime from langflow.services.auth.utils import create_super_user, get_password_hash from langflow.services.database.models.user.user import User -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service import pytest from langflow.services.database.models.user import UserUpdate @pytest.fixture def super_user(client, session): - settings_manager = get_settings_manager() - auth_settings = settings_manager.auth_settings + settings_service = get_settings_service() + auth_settings = settings_service.auth_settings return create_super_user( db=session, username=auth_settings.FIRST_SUPERUSER, @@ -20,8 +20,8 @@ def super_user(client, session): @pytest.fixture def super_user_headers(client, super_user): - settings_manager = get_settings_manager() - auth_settings = settings_manager.auth_settings + settings_service = get_settings_service() + auth_settings = settings_service.auth_settings login_data = { "username": auth_settings.FIRST_SUPERUSER, "password": auth_settings.FIRST_SUPERUSER_PASSWORD, diff --git a/tests/test_vectorstore_template.py b/tests/test_vectorstore_template.py index 87394b890..c0dace665 100644 --- a/tests/test_vectorstore_template.py +++ b/tests/test_vectorstore_template.py @@ -1,14 +1,14 @@ from fastapi.testclient import TestClient -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service # check that all agents are in settings.agents # are in json_response["agents"] def test_vectorstores_settings(client: TestClient, logged_in_headers): - settings_manager = get_settings_manager() + settings_service = get_settings_service() response = client.get("api/v1/all", headers=logged_in_headers) assert response.status_code == 200 json_response = response.json() vectorstores = json_response["vectorstores"] - settings_vecs = set(settings_manager.settings.VECTORSTORES) + settings_vecs = set(settings_service.settings.VECTORSTORES) assert all(vs in vectorstores for vs in settings_vecs) diff --git a/tests/test_websocket.py b/tests/test_websocket.py index 16f9eff05..5016eb704 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -1,7 +1,7 @@ from fastapi import WebSocketDisconnect from fastapi.testclient import TestClient -# from langflow.services.chat.manager import ChatManager +# from langflow.services.chat.manager import ChatService import pytest @@ -28,7 +28,7 @@ def test_init_build(client, active_user, logged_in_headers): def test_websocket_endpoint(client: TestClient, active_user, logged_in_headers): - # Assuming your websocket_endpoint uses chat_manager which caches data from stream_build + # Assuming your websocket_endpoint uses chat_service which caches data from stream_build access_token = logged_in_headers["Authorization"].split(" ")[1] with pytest.raises(WebSocketDisconnect): with client.websocket_connect( @@ -40,12 +40,12 @@ def test_websocket_endpoint(client: TestClient, active_user, logged_in_headers): def test_websocket_endpoint_after_build(client, basic_graph_data): - # Assuming your websocket_endpoint uses chat_manager which caches data from stream_build + # Assuming your websocket_endpoint uses chat_service which caches data from stream_build client.post("api/v1/build/init", json=basic_graph_data) client.get("api/v1/build/stream/websocket_test") # There should be more to test here, but it depends on the inner workings of your websocket handler - # and how your chat_manager and other classes behave. The following is just an example structure. + # and how your chat_service and other classes behave. The following is just an example structure. with pytest.raises(WebSocketDisconnect): with client.websocket_connect("api/v1/chat/websocket_test") as websocket: websocket.send_json({"input": "test"}) From 8d1bff38fe42ddaa7bf08189f93f9f76844a0620 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 15 Sep 2023 17:42:14 -0300 Subject: [PATCH 124/213] =?UTF-8?q?=F0=9F=94=A5=20chore(ChatOutput.py):=20?= =?UTF-8?q?remove=20unused=20ChatOutput=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ChatOutput component was removed as it was no longer being used in the project. This commit removes the file and its associated code. --- .../langflow/components/io/ChatOutput.py | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 src/backend/langflow/components/io/ChatOutput.py diff --git a/src/backend/langflow/components/io/ChatOutput.py b/src/backend/langflow/components/io/ChatOutput.py deleted file mode 100644 index 48fec75bc..000000000 --- a/src/backend/langflow/components/io/ChatOutput.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Optional, Text -from langflow.api.v1.schemas import ChatMessage -from langflow.services.utils import get_chat_service -from langflow import CustomComponent -from anyio.from_thread import start_blocking_portal -from loguru import logger - - -class ChatOutput(CustomComponent): - display_name = "Chat Output" - description = "Used to send a message to the chat." - - field_config = { - "code": { - "show": False, - } - } - - def build_config(self): - return {"message": {"input_types": ["Text"]}} - - def build(self, message: Optional[Text], is_ai: bool = False) -> Text: - if not message: - return "" - try: - chat_service = get_chat_service() - chat_message = ChatMessage(message=message, is_bot=is_ai) - # send_message is a coroutine - # run in a thread safe manner - with start_blocking_portal() as portal: - portal.call(chat_service.send_message, chat_message) - chat_service.chat_history.add_message( - chat_service.cache_service.current_client_id, chat_message - ) - except Exception as exc: - logger.exception(exc) - logger.debug(f"Error sending message to chat: {exc}") - self.repr_value = message - return message From fa1ebea378d043a1f62a48fb0bb218328fd09a3c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 15 Sep 2023 18:16:25 -0300 Subject: [PATCH 125/213] =?UTF-8?q?=F0=9F=90=9B=20fix(process.py):=20remov?= =?UTF-8?q?e=20unnecessary=20session=5Fid=20parameter=20in=20build=5Fsorte?= =?UTF-8?q?d=5Fvertices=20function=20calls=20=F0=9F=90=9B=20fix(endpoints.?= =?UTF-8?q?py):=20remove=20unused=20import=20and=20type=20hinting=20to=20i?= =?UTF-8?q?mprove=20code=20readability=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(endpoints.py):=20fix=20incorrect=20import=20?= =?UTF-8?q?statement=20for=20TaskService=20=F0=9F=90=9B=20fix(endpoints.py?= =?UTF-8?q?):=20fix=20incorrect=20return=20value=20for=20get=5Ftask=5Fstat?= =?UTF-8?q?us=20function=20=E2=9C=A8=20feat(endpoints.py):=20add=20test=20?= =?UTF-8?q?case=20for=20async=20task=20processing=20to=20validate=20task?= =?UTF-8?q?=20completion=20and=20result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 18 ++++++++---- src/backend/langflow/processing/process.py | 4 +-- tests/test_endpoints.py | 32 ++++++++++++++++++++-- tests/utils.py | 19 +++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index d7fed1a70..ec107092b 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import TYPE_CHECKING, Annotated, Any, Optional, Union +from typing import Annotated, Any, Optional, Union from langflow.services.auth.utils import api_key_security, get_current_active_user @@ -33,8 +33,8 @@ from langflow.services.utils import get_session from langflow.worker import process_graph_cached_task from sqlmodel import Session -if TYPE_CHECKING: - from langflow.services.task.manager import TaskService + +from langflow.services.task.manager import TaskService # build router router = APIRouter(tags=["Base"]) @@ -183,11 +183,17 @@ async def process_flow( async def get_task_status(task_id: str): task_service = get_task_service() task = task_service.get_task(task_id) + result = None + if task.ready(): + result = task.result + if isinstance(result, dict) and "result" in result: + result = result["result"] + elif hasattr(result, "result"): + result = result.result + if task is None: raise HTTPException(status_code=404, detail="Task not found") - return TaskStatusResponse( - status=task.status, result=task.result if task.ready() else None - ) + return TaskStatusResponse(status=task.status, result=result) @router.post( diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 877d32df2..6490f02fb 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -94,13 +94,13 @@ def get_build_result(data_graph, session_id): # otherwise, build the graph and return the result if session_id: logger.debug(f"Loading LangChain object from session {session_id}") - result = build_sorted_vertices(data_graph=data_graph, session_id=session_id) + result = build_sorted_vertices(data_graph=data_graph) if result is not None: logger.debug("Loaded LangChain object") return result logger.debug("Building langchain object") - return build_sorted_vertices(data_graph, session_id) + return build_sorted_vertices(data_graph) def load_langchain_object( diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 3344633ab..da67cbb1a 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -8,7 +8,7 @@ from fastapi.testclient import TestClient from langflow.interface.tools.constants import CUSTOM_TOOLS from langflow.template.frontend_node.chains import TimeTravelGuideChainNode -from tests.utils import run_post +from tests.utils import poll_task_status, run_post PROMPT_REQUEST = { @@ -409,11 +409,11 @@ def test_basic_chat_different_session_ids(client, added_flow, created_api_key): # session_id should be returned assert "session_id" in response.json() assert response.json()["session_id"] is not None + session_id1 = response.json()["session_id"] # New request with a different session_id # asking "What is my name?" should return "Gabriel" post_data = { "inputs": {"text": "What is my name?"}, - "session_id": "other session id", } response = client.post( f"api/v1/process/{added_flow.get('id')}", @@ -422,6 +422,7 @@ def test_basic_chat_different_session_ids(client, added_flow, created_api_key): ) assert response.status_code == 200, response.json() assert "Gabriel" not in response.json()["result"]["text"] + assert session_id1 != response.json()["session_id"] def test_basic_chat_with_two_session_ids_and_names(client, added_flow, created_api_key): @@ -448,3 +449,30 @@ def test_basic_chat_with_two_session_ids_and_names(client, added_flow, created_a response_json = run_post(client, flow_id, headers, post_data) assert name in response_json["result"]["text"] + + +# Test function without loop +def test_async_task_processing(client, added_flow, created_api_key): + headers = {"x-api-key": created_api_key.api_key} + post_data = {"inputs": {"text": "Hi, My name is Gabriel"}} + + # Run the /api/v1/process/{flow_id} endpoint with sync=False + response = client.post( + f"api/v1/process/{added_flow.get('id')}", + headers=headers, + json={**post_data, "sync": False}, + ) + assert response.status_code == 200, response.json() + + # Extract the task ID from the response + task_id = response.json().get("id") + assert task_id is not None + + # Polling the task status using the helper function + task_status_json = poll_task_status(client, headers, task_id) + assert task_status_json is not None, "Task did not complete in time" + + # Validate that the task completed successfully and the result is as expected + assert "result" in task_status_json, task_status_json + assert "text" in task_status_json["result"], task_status_json["result"] + assert "Gabriel" in task_status_json["result"]["text"], task_status_json["result"] diff --git a/tests/utils.py b/tests/utils.py index ce913c4b9..a4bae9863 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,3 +1,6 @@ +import time + + def run_post(client, flow_id, headers, post_data): response = client.post( f"api/v1/process/{flow_id}", @@ -6,3 +9,19 @@ def run_post(client, flow_id, headers, post_data): ) assert response.status_code == 200, response.json() return response.json() + + +# Helper function to poll task status +def poll_task_status(client, headers, task_id, max_attempts=20, sleep_time=1): + for _ in range(max_attempts): + task_status_response = client.get( + f"api/v1/task/{task_id}/status", + headers=headers, + ) + if ( + task_status_response.status_code == 200 + and task_status_response.json()["status"] == "SUCCESS" + ): + return task_status_response.json() + time.sleep(sleep_time) + return None # Return None if task did not complete in time From 507d6a756df4db10872f5c5a6467fefae687a25e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 15 Sep 2023 23:24:26 -0300 Subject: [PATCH 126/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20handl?= =?UTF-8?q?e=20None=20key=20in=20=5F=5Fcontains=5F=5F=20method=20to=20prev?= =?UTF-8?q?ent=20Redis=20client=20error=20when=20checking=20if=20key=20exi?= =?UTF-8?q?sts=20in=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index 9042757ae..843eb182e 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -292,7 +292,7 @@ class RedisCache(BaseCacheService, Service): def __contains__(self, key): """Check if the key is in the cache.""" - return self._client.exists(key) + return False if key is None else self._client.exists(key) def __getitem__(self, key): """Retrieve an item from the cache using the square bracket notation.""" From 8987c6d96942318ddfb8f7b88055338b5e67b7aa Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 00:05:03 -0300 Subject: [PATCH 127/213] =?UTF-8?q?=F0=9F=90=9B=20fix(test=5Fagents=5Ftemp?= =?UTF-8?q?late.py):=20fix=20incorrect=20variable=20name=20in=20test=5Fzer?= =?UTF-8?q?o=5Fshot=5Fagent=20function=20=F0=9F=90=9B=20fix(test=5Fendpoin?= =?UTF-8?q?ts.py):=20add=20missing=20pytest.mark.async=5Ftest=20decorator?= =?UTF-8?q?=20to=20test=5Fasync=5Ftask=5Fprocessing=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_agents_template.py | 6 +++--- tests/test_endpoints.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_agents_template.py b/tests/test_agents_template.py index 85f689eda..b12ad7dee 100644 --- a/tests/test_agents_template.py +++ b/tests/test_agents_template.py @@ -31,15 +31,15 @@ def test_zero_shot_agent(client: TestClient, logged_in_headers): } # Additional assertions for other template variables - assert template["callback_service"] == { + assert template["callback_manager"] == { "required": False, "dynamic": False, "placeholder": "", "show": False, "multiline": False, "password": False, - "name": "callback_service", - "type": "BaseCallbackService", + "name": "callback_manager", + "type": "BaseCallbackManager", "list": False, "advanced": False, "info": "", diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index da67cbb1a..cb3755db0 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -452,6 +452,7 @@ def test_basic_chat_with_two_session_ids_and_names(client, added_flow, created_a # Test function without loop +@pytest.mark.async_test def test_async_task_processing(client, added_flow, created_api_key): headers = {"x-api-key": created_api_key.api_key} post_data = {"inputs": {"text": "Hi, My name is Gabriel"}} From f5f89eb8b8990c78ef4ced3c017823d335910312 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 00:05:19 -0300 Subject: [PATCH 128/213] =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20rename?= =?UTF-8?q?=20callback=5Fservice=20to=20callback=5Fmanager=20for=20consist?= =?UTF-8?q?ency=20and=20clarity=20in=20codebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/utils/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index 61b4a93aa..2c8f92ee1 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -30,7 +30,7 @@ def build_template_from_function( variables = {"_type": _type} for class_field_items, value in _class.__fields__.items(): - if class_field_items in ["callback_service"]: + if class_field_items in ["callback_manager"]: continue variables[class_field_items] = {} for name_, value_ in value.__repr_args__(): @@ -84,7 +84,7 @@ def build_template_from_class( if "__fields__" in _class.__dict__: for class_field_items, value in _class.__fields__.items(): - if class_field_items in ["callback_service"]: + if class_field_items in ["callback_manager"]: continue variables[class_field_items] = {} for name_, value_ in value.__repr_args__(): From e9fd9b097fd7d5d8e6febe2554edd95fdcd06be6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 00:05:26 -0300 Subject: [PATCH 129/213] =?UTF-8?q?=F0=9F=90=B3=20chore(docker-compose.yml?= =?UTF-8?q?):=20refactor=20backend=20service=20to=20use=20YAML=20anchors?= =?UTF-8?q?=20and=20aliases=20for=20code=20reuse=20=F0=9F=9A=80=20feat(doc?= =?UTF-8?q?ker-compose.yml):=20add=20tests=20service=20to=20run=20pytest?= =?UTF-8?q?=20with=20coverage=20and=20generate=20reports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index c6a9db73c..e8b999dc7 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -68,7 +68,7 @@ services: # to disable www redirection remove the section: ${STACK_NAME?Variable not set}-www-redirect, - traefik.http.routers.${STACK_NAME?Variable not set}-proxy-http.middlewares=${STACK_NAME?Variable not set}-www-redirect,${STACK_NAME?Variable not set}-https-redirect - backend: + backend: &backend image: "ogabrielluiz/langflow:latest" build: context: ../ @@ -136,7 +136,7 @@ services: test: "exit 0" celeryworker: - image: "ogabrielluiz/langflow:latest" + <<: *backend env_file: - .env build: @@ -149,7 +149,7 @@ services: replicas: 1 flower: - image: "ogabrielluiz/langflow:latest" + <<: *backend env_file: - .env networks: @@ -227,6 +227,17 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-grafana-http.rule=PathPrefix(`/grafana`) - traefik.http.services.${STACK_NAME?Variable not set}-grafana.loadbalancer.server.port=3000 + tests: + <<: *backend + env_file: + - .env + build: + context: ../ + dockerfile: base.Dockerfile + command: pytest -v --cov=langflow --cov-report=term-missing --cov-report=xml --cov-report=html -m async_test + healthcheck: + test: "exit 0" + volumes: grafana_data: app-db-data: From 986b4696cdb8545d032d202802bf543493fe8669 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 00:05:39 -0300 Subject: [PATCH 130/213] =?UTF-8?q?=F0=9F=94=A7=20chore(ci.yml):=20add=20C?= =?UTF-8?q?I/CD=20pipeline=20configuration=20file=20to=20automate=20build?= =?UTF-8?q?=20and=20test=20process=20=F0=9F=94=A7=20chore(pyproject.toml):?= =?UTF-8?q?=20add=20'async=5Ftest'=20marker=20for=20pytest=20to=20identify?= =?UTF-8?q?=20asynchronous=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 63 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 64 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..ace4438ca --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,63 @@ +name: CI/CD Pipeline + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build-and-test: + runs-on: ubuntu-latest + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + steps: + # Step 1: Checkout code from the repository + - name: Checkout code + uses: actions/checkout@v2 + + # Step 2: Setup cache for Docker layers + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + # Step 3: Setup Docker and Docker Compose + - name: Set up Docker + run: docker --version && docker-compose --version + + # Step 4: Build Docker containers and start services (e.g., Redis, Celery) + - name: Build and start services + working-directory: ./deploy + run: docker-compose up --build -d + continue-on-error: true + + - name: Build and run tests + run: | + docker-compose -f docker-compose.yml up --exit-code-from tests tests queue celeryworker db -d + container_id=$(docker ps -a -q --filter "name=tests") + docker cp ${container_id}:/path/in/container/to/test/results ./test-results + + - name: Upload test report + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-report + path: ./test-results + + # Step 6: Capture test results and store as an artifact + - name: Archive test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-results + path: path/to/your/test-results/ # Update this to where pytest stores test results + + # Step 7: Cleanup - Stop and remove Docker containers + - name: Stop services + run: docker-compose down diff --git a/pyproject.toml b/pyproject.toml index 75e838b16..70f5723f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,6 +126,7 @@ testpaths = ["tests", "integration"] console_output_style = "progress" filterwarnings = ["ignore::DeprecationWarning"] log_cli = true +markers = ["async_test"] [tool.ruff] From cc8cd8bef0c1dbc7f9594bc96bf12303aa9f36c3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 00:09:32 -0300 Subject: [PATCH 131/213] =?UTF-8?q?=F0=9F=94=A7=20chore(ci.yml):=20update?= =?UTF-8?q?=20Docker=20commands=20to=20use=20'docker=20compose'=20instead?= =?UTF-8?q?=20of=20'docker-compose'=20for=20consistency=20and=20future=20c?= =?UTF-8?q?ompatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ace4438ca..eea0459d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,30 +34,9 @@ jobs: # Step 4: Build Docker containers and start services (e.g., Redis, Celery) - name: Build and start services working-directory: ./deploy - run: docker-compose up --build -d + run: docker compose up --exit-code-from tests tests queue celeryworker db -d continue-on-error: true - - name: Build and run tests - run: | - docker-compose -f docker-compose.yml up --exit-code-from tests tests queue celeryworker db -d - container_id=$(docker ps -a -q --filter "name=tests") - docker cp ${container_id}:/path/in/container/to/test/results ./test-results - - - name: Upload test report - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-report - path: ./test-results - - # Step 6: Capture test results and store as an artifact - - name: Archive test results - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-results - path: path/to/your/test-results/ # Update this to where pytest stores test results - # Step 7: Cleanup - Stop and remove Docker containers - name: Stop services - run: docker-compose down + run: docker compose down From abc4e8a3e048e0f5078b6a46b4e8975a8d2e6f06 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 00:11:00 -0300 Subject: [PATCH 132/213] =?UTF-8?q?=F0=9F=94=A7=20chore(ci.yml):=20remove?= =?UTF-8?q?=20unnecessary=20comments=20and=20reorganize=20steps=20for=20cl?= =?UTF-8?q?arity=20and=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eea0459d9..bf79d64d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,11 +14,9 @@ jobs: env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} steps: - # Step 1: Checkout code from the repository - name: Checkout code uses: actions/checkout@v2 - # Step 2: Setup cache for Docker layers - name: Cache Docker layers uses: actions/cache@v2 with: @@ -27,16 +25,13 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- - # Step 3: Setup Docker and Docker Compose - name: Set up Docker run: docker --version && docker-compose --version - # Step 4: Build Docker containers and start services (e.g., Redis, Celery) - name: Build and start services working-directory: ./deploy run: docker compose up --exit-code-from tests tests queue celeryworker db -d continue-on-error: true - # Step 7: Cleanup - Stop and remove Docker containers - name: Stop services run: docker compose down From b8d8eccbffb2f601ce6a94936a9c206df2cad22a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:13:08 -0300 Subject: [PATCH 133/213] =?UTF-8?q?=F0=9F=90=9B=20fix(anyio.py):=20add=20e?= =?UTF-8?q?rror=20handling=20when=20launching=20a=20task=20to=20prevent=20?= =?UTF-8?q?crashes=20and=20log=20the=20error=20=F0=9F=90=9B=20fix(celery.p?= =?UTF-8?q?y):=20add=20type=20hinting=20to=20the=20launch=5Ftask=20method?= =?UTF-8?q?=20and=20return=20the=20AsyncResult=20object=20for=20better=20t?= =?UTF-8?q?ask=20tracking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/task/backends/anyio.py | 29 +++++++++++++++---- .../langflow/services/task/backends/celery.py | 9 ++++-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/backend/langflow/services/task/backends/anyio.py b/src/backend/langflow/services/task/backends/anyio.py index 20691212d..6c833443a 100644 --- a/src/backend/langflow/services/task/backends/anyio.py +++ b/src/backend/langflow/services/task/backends/anyio.py @@ -1,6 +1,7 @@ from typing import Any, Callable, Tuple import anyio from langflow.services.task.backends.base import TaskBackend +from loguru import logger class AnyIOTaskResult: @@ -38,13 +39,29 @@ class AnyIOBackend(TaskBackend): async def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any - ) -> Tuple[str, AnyIOTaskResult]: # sourcery skip: remove-unnecessary-cast + ) -> Tuple[str, AnyIOTaskResult]: + """ + Launch a new task in an asynchronous manner. + + Parameters: + task_func: The asynchronous function to run. + *args: Positional arguments to pass to task_func. + **kwargs: Keyword arguments to pass to task_func. + + Returns: + A tuple containing a unique task ID and the task result object. + """ async with anyio.create_task_group() as tg: - task_result = AnyIOTaskResult(tg) - tg.start_soon(task_result.run, task_func, *args, **kwargs) - task_id = str(id(task_result)) - self.tasks[task_id] = task_result - return task_id, task_result + try: + task_result = AnyIOTaskResult(tg) + tg.start_soon(task_result.run, task_func, *args, **kwargs) + task_id = str(id(task_result)) + self.tasks[task_id] = task_result + logger.info(f"Task {task_id} started.") + return task_id, task_result + except Exception as e: + logger.error(f"An error occurred while launching the task: {e}") + return None, None def get_task(self, task_id: str) -> Any: return self.tasks.get(task_id) diff --git a/src/backend/langflow/services/task/backends/celery.py b/src/backend/langflow/services/task/backends/celery.py index 3a6e1f450..f4f81b9cc 100644 --- a/src/backend/langflow/services/task/backends/celery.py +++ b/src/backend/langflow/services/task/backends/celery.py @@ -1,5 +1,5 @@ from typing import Any, Callable -from celery.result import AsyncResult # type: ignore +from celery.result import AsyncResult from langflow.services.task.backends.base import TaskBackend from langflow.worker import celery_app @@ -11,10 +11,13 @@ class CeleryBackend(TaskBackend): def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any ) -> str: + # I need to type the delay method to make it easier + from celery import Task + if not hasattr(task_func, "delay"): raise ValueError(f"Task function {task_func} does not have a delay method") - task = task_func.delay(*args, **kwargs) - return task.id + task: Task = task_func.delay(*args, **kwargs) + return task.id, AsyncResult(task.id, app=self.celery_app) def get_task(self, task_id: str) -> Any: return AsyncResult(task_id, app=self.celery_app) From 05a6f4d0679566a8e19ef0d554e122812f132b13 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:13:53 -0300 Subject: [PATCH 134/213] =?UTF-8?q?=F0=9F=94=A7=20fix(process.py):=20remov?= =?UTF-8?q?e=20unused=20import=20and=20add=20missing=20import=20for=20Base?= =?UTF-8?q?Model=20from=20pydantic=20=F0=9F=94=A7=20fix(manager.py):=20cha?= =?UTF-8?q?nge=20return=20type=20annotation=20of=20launch=5Ftask=20method?= =?UTF-8?q?=20to=20handle=20both=20Coroutine=20and=20Any=20types=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(worker.py):=20add=20missing=20import=20for?= =?UTF-8?q?=20Result=20class=20from=20process=20module=20and=20update=20re?= =?UTF-8?q?turn=20statement=20to=20return=20Result=20object=20as=20diction?= =?UTF-8?q?ary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 8 ++++---- src/backend/langflow/services/task/manager.py | 5 +++-- src/backend/langflow/worker.py | 7 ++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 6490f02fb..0fefa8deb 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass import json from pathlib import Path from langchain.schema import AgentAction @@ -14,6 +13,8 @@ from langchain.chains.base import Chain from langchain.vectorstores.base import VectorStore from typing import Any, Dict, List, Optional, Tuple, Union +from pydantic import BaseModel + def fix_memory_inputs(langchain_object): """ @@ -146,8 +147,7 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): return result -@dataclass -class Result: +class Result(BaseModel): result: Any session_id: str @@ -173,7 +173,7 @@ async def process_graph_cached( # we need to update the cache with the updated langchain_object session_service.update_session(session_id, (langchain_object, artifacts)) - return Result(result, session_id) + return Result(result=result, session_id=session_id) def load_flow_from_json( diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py index e0448ab66..4083978df 100644 --- a/src/backend/langflow/services/task/manager.py +++ b/src/backend/langflow/services/task/manager.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Union +from typing import Any, Callable, Coroutine, Union import logging from langflow.services.base import Service @@ -51,7 +51,8 @@ class TaskService(Service): async def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any ) -> Any: - return await self.backend.launch_task(task_func, *args, **kwargs) + task = self.backend.launch_task(task_func, *args, **kwargs) + return await task if isinstance(task, Coroutine) else task def get_task(self, task_id: Union[int, str]) -> Any: return self.backend.get_task(task_id) diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index ad847a01c..018cfa5e7 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -3,6 +3,7 @@ from typing import Any, Dict, Optional, Tuple from typing import TYPE_CHECKING from celery.exceptions import SoftTimeLimitExceeded # type: ignore from langflow.processing.process import ( + Result, generate_result, process_inputs, ) @@ -44,6 +45,10 @@ def process_graph_cached_task( session_service = get_session_service() if clear_cache: session_service.clear_session(session_id) + if session_id is None: + session_id = session_service.generate_key( + session_id=session_id, data_graph=data_graph + ) # Load the graph using SessionService langchain_object, artifacts = session_service.load_session(session_id, data_graph) processed_inputs = process_inputs(inputs, artifacts) @@ -52,4 +57,4 @@ def process_graph_cached_task( # we need to update the cache with the updated langchain_object session_service.update_session(session_id, (langchain_object, artifacts)) - return result, session_id + return Result(result=result, session_id=session_id).dict() From 43fc6add04b87987a67cdc93f4e04bb81b6a1998 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:15:07 -0300 Subject: [PATCH 135/213] =?UTF-8?q?=F0=9F=90=9B=20fix(conftest.py):=20fix?= =?UTF-8?q?=20typo=20in=20file=20name=20BasicChatWithPromptAndHistory.json?= =?UTF-8?q?=20to=20improve=20consistency=20and=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 6730ac405..61ac84d06 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -31,7 +31,7 @@ def pytest_configure(): Path(__file__).parent.absolute() / "data" / "Openapi.json" ) pytest.BASIC_CHAT_WITH_PROMPT_AND_HISTORY = ( - Path(__file__).parent.absolute() / "data" / "BasicChatWithPromptAndHistory.json" + Path(__file__).parent.absolute() / "data" / "BasicChatwithPromptandHistory.json" ) pytest.CODE_WITH_SYNTAX_ERROR = """ From c367328727d1d883188e5c70605cd63e99a1300c Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:15:23 -0300 Subject: [PATCH 136/213] =?UTF-8?q?=F0=9F=93=A6=20chore(base.Dockerfile):?= =?UTF-8?q?=20add=20tests=20directory=20to=20Docker=20image=20to=20include?= =?UTF-8?q?=20tests=20in=20the=20build=20process?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base.Dockerfile b/base.Dockerfile index c608e8c99..aa50fe90f 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -84,6 +84,8 @@ COPY --from=builder-base $POETRY_HOME $POETRY_HOME COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH COPY ./src/backend ./src/backend +COPY ./tests ./tests + # quicker install as runtime deps are already installed RUN --mount=type=cache,target=/root/.cache \ poetry install --with=dev --extras deploy From 7ef80c8e781058dff85afb20248f7a02056de349 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:16:35 -0300 Subject: [PATCH 137/213] =?UTF-8?q?=F0=9F=94=80=20chore(ci.yml):=20update?= =?UTF-8?q?=20branch=20configuration=20for=20CI/CD=20pipeline=20to=20inclu?= =?UTF-8?q?de=20'dev'=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf79d64d0..76bedb6ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,9 +3,10 @@ name: CI/CD Pipeline on: push: branches: - - main + - dev pull_request: branches: + - dev - main jobs: From ce3127d6f7fcdd689e22b4f9ad3dcb31fe357beb Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:17:59 -0300 Subject: [PATCH 138/213] =?UTF-8?q?=F0=9F=93=A6=20chore(ci.yml):=20rename?= =?UTF-8?q?=20CI/CD=20Pipeline=20workflow=20to=20"Async=20API=20tests"=20f?= =?UTF-8?q?or=20better=20clarity=20and=20specificity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76bedb6ea..fede57ece 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI/CD Pipeline +name: "Async API tests" on: push: From 254d9e946d6d7558722f321c71653aafc1be460b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 16 Sep 2023 11:21:27 -0300 Subject: [PATCH 139/213] =?UTF-8?q?=F0=9F=90=9B=20fix(ci.yml):=20remove=20?= =?UTF-8?q?unnecessary=20-d=20flag=20from=20docker=20compose=20up=20comman?= =?UTF-8?q?d=20to=20prevent=20services=20from=20running=20in=20detached=20?= =?UTF-8?q?mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fede57ece..017f57efe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: - name: Build and start services working-directory: ./deploy - run: docker compose up --exit-code-from tests tests queue celeryworker db -d + run: docker compose up --exit-code-from tests tests queue celeryworker db continue-on-error: true - name: Stop services From 946719eb728f7b01be9d60416b35d89b2d35785d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:28:33 -0300 Subject: [PATCH 140/213] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20chore(pyproject.to?= =?UTF-8?q?ml):=20update=20pandas=20dependency=20to=20version=202.0.3=20fo?= =?UTF-8?q?r=20compatibility=20and=20bug=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 1674 +++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 859 insertions(+), 817 deletions(-) diff --git a/poetry.lock b/poetry.lock index 94e743b13..f292d5352 100644 --- a/poetry.lock +++ b/poetry.lock @@ -414,33 +414,33 @@ files = [ [[package]] name = "black" -version = "23.7.0" +version = "23.9.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"}, - {file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"}, - {file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"}, - {file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"}, - {file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"}, - {file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"}, - {file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"}, - {file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"}, - {file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"}, - {file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"}, - {file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"}, - {file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"}, - {file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"}, - {file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"}, - {file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"}, - {file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, + {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, + {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, + {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, + {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, + {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, + {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, + {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, + {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, + {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, + {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, + {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, ] [package.dependencies] @@ -450,7 +450,7 @@ packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -458,24 +458,6 @@ d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] -[[package]] -name = "bleach" -version = "6.0.0" -description = "An easy safelist-based HTML-sanitizing tool." -optional = false -python-versions = ">=3.7" -files = [ - {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"}, - {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"}, -] - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[package.extras] -css = ["tinycss2 (>=1.1.0,<1.2)"] - [[package]] name = "blinker" version = "1.6.2" @@ -489,93 +471,94 @@ files = [ [[package]] name = "brotli" -version = "1.0.9" +version = "1.1.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" files = [ - {file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"}, - {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"}, - {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"}, - {file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"}, - {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"}, - {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"}, - {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"}, - {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"}, - {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"}, - {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"}, - {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"}, - {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"}, - {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"}, - {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"}, - {file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"}, - {file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"}, - {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a"}, - {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df"}, - {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad"}, - {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde"}, - {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a"}, - {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f"}, - {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d"}, - {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f"}, - {file = "Brotli-1.0.9-cp311-cp311-win32.whl", hash = "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d"}, - {file = "Brotli-1.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679"}, - {file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"}, - {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"}, - {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"}, - {file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"}, - {file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"}, - {file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"}, - {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"}, - {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"}, - {file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"}, - {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"}, - {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"}, - {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"}, - {file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"}, - {file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"}, - {file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"}, - {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"}, - {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"}, - {file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"}, - {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"}, - {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"}, - {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"}, - {file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"}, - {file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"}, - {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"}, - {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"}, - {file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"}, - {file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"}, - {file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"}, - {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"}, - {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"}, - {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"}, - {file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"}, - {file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"}, - {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"}, - {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"}, - {file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"}, - {file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"}, - {file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"}, - {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"}, - {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"}, - {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"}, - {file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"}, - {file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"}, - {file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"}, - {file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7"}, - {file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019"}, - {file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"}, - {file = "Brotli-1.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8"}, - {file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337"}, - {file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be"}, - {file = "Brotli-1.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a"}, - {file = "Brotli-1.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be"}, - {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a"}, - {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7"}, - {file = "Brotli-1.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755"}, - {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"}, + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, + {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, + {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, + {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, + {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, + {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, + {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, + {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, + {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, + {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, + {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, + {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, + {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, + {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, + {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, + {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, + {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, + {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, + {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, + {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, + {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, + {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, ] [[package]] @@ -935,76 +918,76 @@ testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] [[package]] name = "clickhouse-connect" -version = "0.6.11" +version = "0.6.12" description = "ClickHouse Database Core Driver for Python, Pandas, and Superset" optional = false python-versions = "~=3.7" files = [ - {file = "clickhouse-connect-0.6.11.tar.gz", hash = "sha256:f36395c8670f6b2e6ed5d2fd979f05b2eac3e395e8c3e4dbf90c41b3f8ff0b2d"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:639ebbef11a7c11c7c9c1f51c1c3c2eaadf550df960d14a2de124d8186f8f2d7"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74cb1753ab53a1109196f82d348c4d080e81419a1e3ce89e7c1677d7fab3cafe"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c55b1b05248d2af6fb21ece4ce102db92ac82d47eebbaee649148644c84e21a6"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c363a71a4bfce23d2936b4c69d03c382bee6a7decf7772517b91ea83751a619c"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:968e9081a6c5088c4c7749047d7e442d49f3ec75a34c662d80727348769d3702"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a97a72dac23563bd1cf3b6e2bdc440fc140e024cd7c2e8b0cd4de5fe2ea4eca9"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:47af3c0cd6dab32135abbdcf0e122a19656133940c9d415922ce570162235308"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f6cbb70362171dba25bc2d5390d7d9706a261c5ac9852335132049c91f97ea2f"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-win32.whl", hash = "sha256:7d9a414a738ec0f30ce75117e8014b0e8ebf2bfe436c475c4664d7f6367b488c"}, - {file = "clickhouse_connect-0.6.11-cp310-cp310-win_amd64.whl", hash = "sha256:bf839ca651fa550c68fc7d2fae023ca260522685c113503b89e3487c46175618"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c48f1f0bb56de86c370f918647fcbfe973f0366579a558e5a230f392a5f61487"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bece8b35f8ffdf3354ce21dae1d246b6e88402b852bac2a00f4ee24ac94a9dbd"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdf44e4ed8acd11709dce1f52e0aa55463c090930708cf3ebe8ca975a5adb4a"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a009a1681a7f01760d9c6706183b146a0c6f67b58bcb4f2443a3caf498464830"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b40c0e6866d80d0fcecad1c34c3a86104e3676d08a5a3979c82202840dee5662"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:63faf2341ee6e7447a31bfc648fd5cbf8d54ed5c1c5be8270ff70f6c3864e622"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:00c260af72909df01e1c27325562d1b72e43e6136b0164d2735b8820e4e0cf28"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e017a14ee573687a9f8bc739c91b23f4f8d3a464aa13cf690b78c8b2a236143"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-win32.whl", hash = "sha256:b290d7d644222dcc6ec07efb542be193f35b79b8fc3cca027be684508f011d92"}, - {file = "clickhouse_connect-0.6.11-cp311-cp311-win_amd64.whl", hash = "sha256:08795de53a508b284e153d823b9c4373c02cb991d09403a214ffa39f0f04b866"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:502ef4bae3dac9323726c89df2be63858d6aaa0c71907046ab8062038b6b6f8b"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e74ab6e35efbecabce4ac535aa30d54e914b975e8375212525e76d264790d47f"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38fb854cf15ffa1eac5ce5554237447cf6766fbe0cd265f057821178d98d76c8"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe4f1fdc808c472affba07a7117b5aa51536bc313cc47d1e8185bb47d2fa290d"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8ade3b7d53396c78acd6018ad5b69ea2717224065c3fd1863cf4c56dfb19666a"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2f25606b3ccc6bbda34279d21ad51b599bca6b983162e5c9f4304e537e00ca64"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8dc0ed0c48ca8a1eece3ace9aa57f733f4fcb93dbb150d18551f0cda3ff4531f"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-win32.whl", hash = "sha256:8927eeb6d4202b72cc42d0df5efba79168ff40b27d836ee48713cb832cde6f8e"}, - {file = "clickhouse_connect-0.6.11-cp37-cp37m-win_amd64.whl", hash = "sha256:c7235debe31d63e4b5c6e1cb954055e708c24b2617ff6e98807ad0c5eb51a345"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0e3faf7a482e14050f2461114b169aea5d1c642e87d54b211742ff29e4c8fe6f"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b6519454eda26e943565f163933e82ebe075c628351bda44354f5c3f03cf5b20"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9597ad4551af5b0f5bc26866dfa1e82cd398cd1298637a1df3ffe2612993bf"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aa4cc8a096fd3eaed1bd536b993fae0ebadd373e24910f813ba11f3c209a5f3"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9fcb18ed7443ee1c466fc4585e7060ce5208fe371f3be77d334446b548d0bc43"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f7c4e88fce75fa0de1a10b2f6f5cd39fac796827a989612338aa04d2c422b3f7"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f7268eef26162cdbf7374804b3a9bfaedbd6141abac125452f0563fe6942e29a"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0069931dfef4dc4d30cb55686473afb9572b1f1d6f249e3d64b02dc0782d30e2"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-win32.whl", hash = "sha256:7cc72769a100ad20f8ca4597acf53b84afa20ce76a2fc760c21f3abbcb20cb94"}, - {file = "clickhouse_connect-0.6.11-cp38-cp38-win_amd64.whl", hash = "sha256:fcd83d48095f1d83dc6a2245d0276592b11bd58e6e398430d1c1cb62d61610cd"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b465566c9d973b9fed03244ac92440c9a7486af5b224e4d11bf7497ff1953f0"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3130ffd160ee6b809de110c01a32fc07a3026ab0438b9f3ff2216c79511d8304"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43e5f3075d644438c4bb9393672ec739e7c1e7c497d4e02d0a0e3a9a205b4d40"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a00264c6539f42074ab6cad22634fafecba75f696569956f0eaad36624b8d5d"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c79c74e18da1d129c96c479c2b17694bed62f7008a4da6129317a2f11e62f7e4"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:66ca83a2ccceb5b70a93288761e6c6279b9b72489ba64ba1ba378448b0c68563"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:697e86c249a8d613a0aac4b91ee4311bcdf3a2b5e1ce58a796de454549dd5e86"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:35846e0503e2d7794b5e81109b1ec3958facbfb5e28997f55b29fc8c37374376"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-win32.whl", hash = "sha256:afd50273adef2ff33cf0a9d176bd19964dfb6cb970b465a82903a1adf291aaf0"}, - {file = "clickhouse_connect-0.6.11-cp39-cp39-win_amd64.whl", hash = "sha256:6d1faf4877799e4f1baeeea44d9bb52f7cd8421fc9a1c871fc01721dfe879cc4"}, - {file = "clickhouse_connect-0.6.11-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b6f8ada7066fcbd7cea2e644c8a1e2af81dd39547ac60fde570732e896fc4cf0"}, - {file = "clickhouse_connect-0.6.11-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b31f978ee92a0e53bdc920e1c59df20569c8e7b5920043d310bcd87d525db30"}, - {file = "clickhouse_connect-0.6.11-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39e1122f75ec97c80395e5a98ffb7cbb4a036bb2f05aa425b8ec048126cfafb2"}, - {file = "clickhouse_connect-0.6.11-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f4bbcf3bdf5937d0c3ca9a0d2ad7c539d6d89415598059f8ad60d550b1fa171"}, - {file = "clickhouse_connect-0.6.11-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a096186cad5475c9ec05cf06e6efd599f2109db734bd7bf8a643fc008916bdf8"}, - {file = "clickhouse_connect-0.6.11-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:63df2068806884ed0898170e6532bb43c1be029cfc4a34ba63c0b05120fb5680"}, - {file = "clickhouse_connect-0.6.11-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e870bad786ee7223676fb01c23f704eccd7cf7af0a4b2c94cef812585551c3a"}, - {file = "clickhouse_connect-0.6.11-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d94e212b1a29d2d6ce071a82574a96925e0e2069d02193c48acf38e5208394cf"}, - {file = "clickhouse_connect-0.6.11-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f12881721752df397396d9a4240b7a2bc5c32fd316766ec50b95537a852a1b6f"}, - {file = "clickhouse_connect-0.6.11-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c2e10d0f718612f2a7e903de02a8921b47bd2fcb1ad1de880506f389090cf262"}, - {file = "clickhouse_connect-0.6.11-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6da4260307a7524efb0f2c797f3cc2e2a89fd3fadddc43b6ef825cadbbc5efd"}, - {file = "clickhouse_connect-0.6.11-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39f59b77962e7c3861692a6cc11ff59b9354e85a99d432c7f04371a3cdbecba"}, - {file = "clickhouse_connect-0.6.11-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc3f23c81203178a2d8047117b0cb6e0e969d7cc4ecff61932b971c9452ffef"}, - {file = "clickhouse_connect-0.6.11-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38084d7d0202e88606f70cf6795830818234a8343c8a29bfeb5a1c34bb4e8315"}, - {file = "clickhouse_connect-0.6.11-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:117e31f41c6b6fa46577bb445d50ae6faa080d780f5484e1d302ae685eb349a3"}, + {file = "clickhouse-connect-0.6.12.tar.gz", hash = "sha256:32078b9f48a57d58f12de5bc825413c84b862ee8aa2b773c38d4c3c3c9d4c614"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39993b493b764fb93021cb7b93ba721872d4eaaf2e6252a52a558172b4c6e8b0"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b900c09900b3ca64d77c8eeceb4eeea604c5ccb2455000cf31fd92ac28825d7d"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9f967ae9c7df0f5950e5f8f5433118f40bd50b9db21a7db1b1e51c4c38de64"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fbb527accdc5845eab76268be15e928a8b4e931e967adf7886bdab37f5b1822"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c2f1f0d83e59480af4f5af62e69a69152c2de5c32abcf192e4b1bac6d6546b8"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f4b46a8605aebd676153f79fe8d049a0241e312bb987190fef9dee01dbefdab4"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a58bab7c217254c4ec0940659ab998d2d399e0ea62151e99745eba0586741d30"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25fdf56cb6ea3ab0d52688bd28eb80e72045db7584dbf272178f980aa81d63b4"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-win32.whl", hash = "sha256:5fac0b3a3549cdb800d0e96901990723dc0b1e6e3dd937922ce40d64bc6c2d87"}, + {file = "clickhouse_connect-0.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:9e30634f3636249f3bed2d8e4e86bced3e0fdf3aa8dd6c7c239907b21f4ba0fd"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:55bda2f883dbb5ef6967a29c47ed1c1cc7333f3d546a1722094ebf508ad378eb"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d739cfd3ea279634eb29242da38017e464e9186aeb8eb79ba6037408adc6bde"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fb5a5271a487aad3607db42f650efbaaf28c9be53ded7936c3c163855ad148"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdde6e9b7b5266af3f1aa0e64e2bf2d1c9a0e9f1e013a5722267160a418f14b6"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7b9a77f2e203d4ba000bfdc335548f53501334d7ecaf53b769f768cb3acef77"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c6b9d637d7177ece6dfd29ba75f34209f793493b6449cfcb1ae577b98e217d24"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:dd70bf0e00bbfaa85cce37716703e874b2d1fdd712960aa3cd788f6925864d5b"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3aa3925468c31cfb83466f21efcf1c10b14afc0c4d746b10198da6578afee977"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-win32.whl", hash = "sha256:39a14f315674df5a9ba83d17f95d34af49935c5ba991ee5d397abdc4ce48c458"}, + {file = "clickhouse_connect-0.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:9725715b2d464b47f71c26a9211716b6186a0c3714252ef21cde50ca43c34a3e"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8a33d424b0de55e3d854af83c12ebf8b8c22401ec24b877f8775d3c38ed4be36"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbac4e2f8d4dbe94cf1827ea50be82361826729099321c4a216bfbdc85f7d770"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cfaf7428d75e079223142c6db643a6a1aeb1dba10b24489f061765e986da240"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a49a5ff24cc4f47faab7b623740aafd31fc5146d7a74a564a7f1c92f6463c8"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99966aa95cd03007043d42c87f1caef70a9b8f289a155cb47adbdba2b9b09375"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e3f43ab9bab1a192fa4030fc38d791d6d9c77cfefc1ff9794fb6e643c0d03fea"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:321fab42eff5af20eae10c3a4df440ad4974e3ca80f3f944cf4f7fd09c537185"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-win32.whl", hash = "sha256:709cd1a6a4a7b138be0a531bc1ccdbb0a4878810074cd1d2b92ba8a3e76f2cfa"}, + {file = "clickhouse_connect-0.6.12-cp37-cp37m-win_amd64.whl", hash = "sha256:cd1cc12fea9d2522c845884860a4e643e706e29e3184c749c25d5778314f4775"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:78b1ee7fc084808df595c2ecb5ca03f6b964fdf9a9b3ebf6b5724aa3f3381067"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:70158813a355aa5715219439095e459743a15da4166359c8e944a2a878e16154"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e14c3cff4dfbc2efb0463eb455553f80a95d5cd932d5880fdec0f833066c34"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d40e0433d06009ed98a0629dae909f5d8ab910e9353c4b9eb6ddf538ef12f6e"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c4f0a0aa32d66244996bf3d882b3d48c1fa0dc67757dddeaf5e0e62689e59be"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2556346e7210fc9d38101ebe2e4f642f1dcc871c67d1f3aa121b70321f9b915e"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:27bcedf49e9c3388a5ae74c9d99191d969371d17adaee3e52da38f1d34777164"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:acacf57500802d7fd3f74be9dfabbcbece2ea63f86dc91a34328d6ea118c1355"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-win32.whl", hash = "sha256:16a7b88d1021dd1855ede700d10faafe234dd7f64b371bb201770c01cbdf2165"}, + {file = "clickhouse_connect-0.6.12-cp38-cp38-win_amd64.whl", hash = "sha256:5398a91f6b90efc91eee2d0947544ced56b322a77f33ee73c6d3aa5c5751d3ec"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d81668c1dd98177fe8dd256235ef496570fa4ab6381325c133518c5503474d9"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2ee0ae56ec5673c8442efa9b0505d073cd950f0d2034e09c9f95a92472170b9b"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c5d7e0b92ef45554f17169f51b548a53f7186af0ec3553bf42227e3fc2e7de"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62bb5baf27d7a6acee79f9314d41b9278e089a696fc3794954a4c186e0f91e48"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17864ec2e9bd602930ff2c42c6d8c1c4d7e2d23b77478148c3d2614e54a7158"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c6efb3f1b6a98e4fde02be55a33cc34572f9db4bd9d9362c8075cc4163431e86"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:40ac6f576e36813ecb412fb5f4e786a235d048e29a47b97f378ff39e7fcbbf58"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bdf52a8bf51ec7674aa90b96fdbea51dad61ac1b673a1ef3e69cea88ea8f1815"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-win32.whl", hash = "sha256:290e59694739fdcaacec5f62cae4a8ce7e5768e1a04c70b013c6bcf4e40448f6"}, + {file = "clickhouse_connect-0.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:f43dbdb491be7921e246fa4fa43eb8de7377c2924a89c9dba11ab1d66bcb3f49"}, + {file = "clickhouse_connect-0.6.12-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a69dc6dab75ad53c2690f74671440be68b329eadf95876f2fcbbdd731b2be742"}, + {file = "clickhouse_connect-0.6.12-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be8bf29d7f80ed8a78c6453b13ae16f7b1244ad3177994c5e3e1d2c767b097b4"}, + {file = "clickhouse_connect-0.6.12-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:083d2bcb35a6fe0068b2c58533d4c008176e0a450f737c9bd508083324bb3105"}, + {file = "clickhouse_connect-0.6.12-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3483c0c4d53a540a32e6da97e2305df4303986e8f0ddbf31d4d811beeb229a5"}, + {file = "clickhouse_connect-0.6.12-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:7dfeae1060e4988e2bf5b146fe523cfe5cfece63805a57b8aad9e4f4d521f67b"}, + {file = "clickhouse_connect-0.6.12-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:78b7ad5536edd07958dc645023e7eca779051d6c0c5a251f9459e050b54eaaac"}, + {file = "clickhouse_connect-0.6.12-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:166af2f4b9b1443a55cd8e56d13a67d7f2c3131ff2fbb60e82307bec74c8bfc0"}, + {file = "clickhouse_connect-0.6.12-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d0ed56d6c7aaabedae107c82d3a941dd76c1048400a0a57dac9c798ca297f1c"}, + {file = "clickhouse_connect-0.6.12-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a7ec15c5fb2aea8e7fcd0a51e8ab559d24a8e0e77b837e2377863d26a036a1a"}, + {file = "clickhouse_connect-0.6.12-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ad918a6c67cf8dc68e1b301f80eb58157eabfca569cc40d2dadea54331cf6ef0"}, + {file = "clickhouse_connect-0.6.12-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:593f60be742b61e6433fa123da6ac055bb074b16c6a8162f7d40c5c52441cccb"}, + {file = "clickhouse_connect-0.6.12-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:004b4ce12f22366a027ce6f25106940eda7fecfe02131bf882e2af07ec7708cf"}, + {file = "clickhouse_connect-0.6.12-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75cb077c748389bd197ba93b10efd482a5e3482dcebd81ed74dda825f5e942b2"}, + {file = "clickhouse_connect-0.6.12-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d05e9e156d24d9ede663a99a1b54ae5e5b2424f4809f7f0f05d3fb33c2b23e02"}, + {file = "clickhouse_connect-0.6.12-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3043aa3d475c66a4583208ac8511ca97d986b76b5dc072ef2961645796dec0cc"}, ] [package.dependencies] @@ -1023,19 +1006,18 @@ sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"] [[package]] name = "cohere" -version = "4.22" +version = "4.17.0" description = "" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "cohere-4.22-py3-none-any.whl", hash = "sha256:e09cc3967c34a15cde63e29783c05bfe80617b2a60817ad1d13c12845f6e71b9"}, - {file = "cohere-4.22.tar.gz", hash = "sha256:aa235ea8d598bde03c2a5c6671f2785b6707a6f297c765ea87422f62f19a9021"}, + {file = "cohere-4.17.0-py3-none-any.whl", hash = "sha256:44e0bdb0a2d9467506d27b285f542177b98f92647f27e17ea921a01006fe2f33"}, + {file = "cohere-4.17.0.tar.gz", hash = "sha256:9f479543b50490b4cb6385468d7571ad891a09cde7bd6b028171596bac6ce6ff"}, ] [package.dependencies] aiohttp = ">=3.0,<4.0" backoff = ">=2.0,<3.0" -fastavro = {version = "1.8.2", markers = "python_version >= \"3.8\""} importlib_metadata = ">=6.0,<7.0" requests = ">=2.25.0,<3.0.0" urllib3 = ">=1.26,<3" @@ -1104,63 +1086,63 @@ yaml = ["PyYAML"] [[package]] name = "coverage" -version = "7.3.0" +version = "7.3.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5"}, - {file = "coverage-7.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637"}, - {file = "coverage-7.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af"}, - {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1"}, - {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12"}, - {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689"}, - {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977"}, - {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51"}, - {file = "coverage-7.3.0-cp310-cp310-win32.whl", hash = "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527"}, - {file = "coverage-7.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1"}, - {file = "coverage-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f"}, - {file = "coverage-7.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d"}, - {file = "coverage-7.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd"}, - {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7"}, - {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a"}, - {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74"}, - {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214"}, - {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f"}, - {file = "coverage-7.3.0-cp311-cp311-win32.whl", hash = "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482"}, - {file = "coverage-7.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70"}, - {file = "coverage-7.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b"}, - {file = "coverage-7.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446"}, - {file = "coverage-7.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071"}, - {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe"}, - {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a"}, - {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873"}, - {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2"}, - {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b"}, - {file = "coverage-7.3.0-cp312-cp312-win32.whl", hash = "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321"}, - {file = "coverage-7.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479"}, - {file = "coverage-7.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1"}, - {file = "coverage-7.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd"}, - {file = "coverage-7.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e"}, - {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54"}, - {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254"}, - {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0"}, - {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84"}, - {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985"}, - {file = "coverage-7.3.0-cp38-cp38-win32.whl", hash = "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9"}, - {file = "coverage-7.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543"}, - {file = "coverage-7.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba"}, - {file = "coverage-7.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393"}, - {file = "coverage-7.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28"}, - {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95"}, - {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a"}, - {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34"}, - {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e"}, - {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54"}, - {file = "coverage-7.3.0-cp39-cp39-win32.whl", hash = "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3"}, - {file = "coverage-7.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e"}, - {file = "coverage-7.3.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0"}, - {file = "coverage-7.3.0.tar.gz", hash = "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865"}, + {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, + {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, + {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, + {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, + {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, + {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, + {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, + {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, + {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, + {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, + {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, + {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, + {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, + {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, + {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, + {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, + {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, + {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, + {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, + {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, + {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, + {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, ] [package.dependencies] @@ -1216,13 +1198,13 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "ctransformers" -version = "0.2.26" +version = "0.2.27" description = "Python bindings for the Transformer models implemented in C/C++ using GGML library." optional = true python-versions = "*" files = [ - {file = "ctransformers-0.2.26-py3-none-any.whl", hash = "sha256:7d1769e16c62e8105ba162cc04151b53953a7f959117766b3da614ec74cfbb37"}, - {file = "ctransformers-0.2.26.tar.gz", hash = "sha256:95a4d97c9ff33a78f7db6dc1186ee9a8be50c3cc3a73c1f667cba6255e260e7a"}, + {file = "ctransformers-0.2.27-py3-none-any.whl", hash = "sha256:6a3ba47556471850d95fdbc59299a82ab91c9dc8b40201c5e7e82d71360772d9"}, + {file = "ctransformers-0.2.27.tar.gz", hash = "sha256:25653d4be8a5ed4e2d3756544c1e9881bf95404be5371c3ed506a256c28663d5"}, ] [package.dependencies] @@ -1251,29 +1233,29 @@ typing-inspect = ">=0.4.0,<1" [[package]] name = "debugpy" -version = "1.6.7.post1" +version = "1.8.0" description = "An implementation of the Debug Adapter Protocol for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "debugpy-1.6.7.post1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:903bd61d5eb433b6c25b48eae5e23821d4c1a19e25c9610205f5aeaccae64e32"}, - {file = "debugpy-1.6.7.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16882030860081e7dd5aa619f30dec3c2f9a421e69861125f83cc372c94e57d"}, - {file = "debugpy-1.6.7.post1-cp310-cp310-win32.whl", hash = "sha256:eea8d8cfb9965ac41b99a61f8e755a8f50e9a20330938ad8271530210f54e09c"}, - {file = "debugpy-1.6.7.post1-cp310-cp310-win_amd64.whl", hash = "sha256:85969d864c45f70c3996067cfa76a319bae749b04171f2cdeceebe4add316155"}, - {file = "debugpy-1.6.7.post1-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:890f7ab9a683886a0f185786ffbda3b46495c4b929dab083b8c79d6825832a52"}, - {file = "debugpy-1.6.7.post1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4ac7a4dba28801d184b7fc0e024da2635ca87d8b0a825c6087bb5168e3c0d28"}, - {file = "debugpy-1.6.7.post1-cp37-cp37m-win32.whl", hash = "sha256:3370ef1b9951d15799ef7af41f8174194f3482ee689988379763ef61a5456426"}, - {file = "debugpy-1.6.7.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:65b28435a17cba4c09e739621173ff90c515f7b9e8ea469b92e3c28ef8e5cdfb"}, - {file = "debugpy-1.6.7.post1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:92b6dae8bfbd497c90596bbb69089acf7954164aea3228a99d7e43e5267f5b36"}, - {file = "debugpy-1.6.7.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72f5d2ecead8125cf669e62784ef1e6300f4067b0f14d9f95ee00ae06fc7c4f7"}, - {file = "debugpy-1.6.7.post1-cp38-cp38-win32.whl", hash = "sha256:f0851403030f3975d6e2eaa4abf73232ab90b98f041e3c09ba33be2beda43fcf"}, - {file = "debugpy-1.6.7.post1-cp38-cp38-win_amd64.whl", hash = "sha256:3de5d0f97c425dc49bce4293df6a04494309eedadd2b52c22e58d95107e178d9"}, - {file = "debugpy-1.6.7.post1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:38651c3639a4e8bbf0ca7e52d799f6abd07d622a193c406be375da4d510d968d"}, - {file = "debugpy-1.6.7.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:038c51268367c9c935905a90b1c2d2dbfe304037c27ba9d19fe7409f8cdc710c"}, - {file = "debugpy-1.6.7.post1-cp39-cp39-win32.whl", hash = "sha256:4b9eba71c290852f959d2cf8a03af28afd3ca639ad374d393d53d367f7f685b2"}, - {file = "debugpy-1.6.7.post1-cp39-cp39-win_amd64.whl", hash = "sha256:973a97ed3b434eab0f792719a484566c35328196540676685c975651266fccf9"}, - {file = "debugpy-1.6.7.post1-py2.py3-none-any.whl", hash = "sha256:1093a5c541af079c13ac8c70ab8b24d1d35c8cacb676306cf11e57f699c02926"}, - {file = "debugpy-1.6.7.post1.zip", hash = "sha256:fe87ec0182ef624855d05e6ed7e0b7cb1359d2ffa2a925f8ec2d22e98b75d0ca"}, + {file = "debugpy-1.8.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb"}, + {file = "debugpy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada"}, + {file = "debugpy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f"}, + {file = "debugpy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637"}, + {file = "debugpy-1.8.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e"}, + {file = "debugpy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6"}, + {file = "debugpy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b"}, + {file = "debugpy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153"}, + {file = "debugpy-1.8.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd"}, + {file = "debugpy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f"}, + {file = "debugpy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa"}, + {file = "debugpy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595"}, + {file = "debugpy-1.8.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8"}, + {file = "debugpy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332"}, + {file = "debugpy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6"}, + {file = "debugpy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926"}, + {file = "debugpy-1.8.0-py2.py3-none-any.whl", hash = "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4"}, + {file = "debugpy-1.8.0.zip", hash = "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0"}, ] [[package]] @@ -1656,36 +1638,36 @@ all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" [[package]] name = "fastavro" -version = "1.8.2" +version = "1.8.3" description = "Fast read/write of AVRO files" optional = false python-versions = ">=3.8" files = [ - {file = "fastavro-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:0e08964b2e9a455d831f2557402a683d4c4d45206f2ab9ade7c69d3dc14e0e58"}, - {file = "fastavro-1.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:401a70b1e5c7161420c6019e0c8afa88f7c8a373468591f5ec37639a903c2509"}, - {file = "fastavro-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef1ed3eaa4240c05698d02d8d0c010b9a03780eda37b492da6cd4c9d37e04ec"}, - {file = "fastavro-1.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:543185a672ff6306beb329b57a7b8a3a2dd1eb21a5ccc530150623d58d48bb98"}, - {file = "fastavro-1.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ffbf8bae1edb50fe7beeffc3afa8e684686550c2e5d31bf01c25cfa213f581e1"}, - {file = "fastavro-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:bb545eb9d876bc7b785e27e98e7720ada7eee7d7a1729798d2ed51517f13500a"}, - {file = "fastavro-1.8.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b837d3038c651046252bc92c1b9899bf21c7927a148a1ff89599c36c2a331ca"}, - {file = "fastavro-1.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3510e96c0a47e4e914bd1a29c954eb662bfa24849ad92e597cb97cc79f21af7"}, - {file = "fastavro-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccc0e74f2c2ab357f39bb73d67fcdb6dc10e23fdbbd399326139f72ec0fb99a3"}, - {file = "fastavro-1.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:add51c70d0ab1175601c75cd687bbe9d16ae312cd8899b907aafe0d79ee2bc1d"}, - {file = "fastavro-1.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d9e2662f57e6453e9a2c9fb4f54b2a9e62e3e46f5a412ac00558112336d23883"}, - {file = "fastavro-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:fea75cf53a93c56dd56e68abce8d314ef877b27451c870cd7ede7582d34c08a7"}, - {file = "fastavro-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:f489020bb8664c2737c03457ad5dbd490579ddab6f0a7b5c17fecfe982715a89"}, - {file = "fastavro-1.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a547625c138efd5e61300119241041906ee8cb426fc7aa789900f87af7ed330d"}, - {file = "fastavro-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53beb458f30c9ad4aa7bff4a42243ff990ffb713b6ce0cd9b360cbc3d648fe52"}, - {file = "fastavro-1.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7b1b2cbd2dd851452306beed0ab9bdaeeab1cc8ad46f84b47cd81eeaff6dd6b8"}, - {file = "fastavro-1.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d29e9baee0b2f37ecd09bde3b487cf900431fd548c85be3e4fe1b9a0b2a917f1"}, - {file = "fastavro-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:66e132c710663230292bc63e2cb79cf95b16ccb94a5fc99bb63694b24e312fc5"}, - {file = "fastavro-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:38aca63ce604039bcdf2edd14912d00287bdbf8b76f9aa42b28e6ca0bf950092"}, - {file = "fastavro-1.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9787835f6449ee94713e7993a700432fce3763024791ffa8a58dc91ef9d1f950"}, - {file = "fastavro-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536cb448bc83811056be02749fd9df37a69621678f02597d272970a769e9b40c"}, - {file = "fastavro-1.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e9d5027cf7d9968f8f819958b41bfedb933323ea6d6a0485eefacaa1afd91f54"}, - {file = "fastavro-1.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:792adfc0c80c7f1109e0ab4b0decef20691fdf0a45091d397a0563872eb56d42"}, - {file = "fastavro-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:650b22766259f7dd7519dfa4e4658f0e233c319efa130b9cf0c36a500e09cc57"}, - {file = "fastavro-1.8.2.tar.gz", hash = "sha256:ab9d9226d4b66b6b3d0661a57cd45259b0868fed1c0cd4fac95249b9e0973320"}, + {file = "fastavro-1.8.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:0e7d98e27cfff61befa23b11422e72a9516fe571d87fd41a656074a958d1f5df"}, + {file = "fastavro-1.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60bfc8b6d3a3e27ae68ce952f9c7a63001dd82f96a519ff25d105a2b61b4bae9"}, + {file = "fastavro-1.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d107cde443d92be8b65007ef304a602702853925c4b9ce63b66b8cdf04938af0"}, + {file = "fastavro-1.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf02deeea731910d55e24f3b44a848007b600ddd0b8861dab9075aa116b0da1"}, + {file = "fastavro-1.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b30bbe06289310ff60c32c0ab01c394354f5bcae421842f06915ee7e401232ee"}, + {file = "fastavro-1.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:c38ba23be298b1df63eaadf0663e8c1dc3fe355608ba3ce769554f61cc20f2d8"}, + {file = "fastavro-1.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4e5d8ad6dcf7cc4e15fc5f30344e4fcb1bac5c0c1b48ae88a46ceef470c04b0c"}, + {file = "fastavro-1.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bf6f1debda2bf84f57bdeee289e38e1ea8b23722792b7bdec8be6b3bf4dac67"}, + {file = "fastavro-1.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb2702976cf9bf4e1c66bae3534f498a93272eaa4cf2ba24fe18aa29c5fab647"}, + {file = "fastavro-1.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfc73765b294ef56f71c1bb064ee81efa1da13bb0b1134dd53674bbb89477c78"}, + {file = "fastavro-1.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fa282c78485be06df1e25f0c9b6837de520a22838e7c9af95b58fc68c6c9ce34"}, + {file = "fastavro-1.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:65e59420ce7a8cbb256363b9bc2b98fcd0c220723ec50541aa0aaf137dfa21fb"}, + {file = "fastavro-1.8.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4416fdf69c82364d737e77c2a6ab06eeb20375d84813c061789e20bc047132a5"}, + {file = "fastavro-1.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f00ead48738e882832cc7ad87060365eb3eeace196ff9a5905a4caf0bab351"}, + {file = "fastavro-1.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ce57e57ec56a235ab012fde3ce7eaa3846980a9026448fcb32cb065f2460514"}, + {file = "fastavro-1.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b74fbcf860084576bd773169a18ddd140d06f9e85bb622756f557023947f179f"}, + {file = "fastavro-1.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9164d7cb1541d15587c3a446f17719bc1f20008a1df1583e55d8b5a323266b5"}, + {file = "fastavro-1.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:7ca4e19db2ded435dd393f58f65297102e7329ca8ba31d03be9c480b34be9123"}, + {file = "fastavro-1.8.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:821b1de9785e266142a8658e25df52bceaa40936c087925688a4fad4dee0beb0"}, + {file = "fastavro-1.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0116bf82a10eb3553d61d6d884f18c8b21049463fdefaaea9275d8bad64a0f5b"}, + {file = "fastavro-1.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6035368cbcbbb1063c2d1ce763ed5a602f4b6af13b325e77a6b61e45f8172067"}, + {file = "fastavro-1.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c0c194340ad5c6a5a5347ba0170d1413c149cd87faddcc519d9fcdedadaa1619"}, + {file = "fastavro-1.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7d2c093be35bcf77547d5cdeeefae2c18b88ae529fa3866da81f5c7c342fceb3"}, + {file = "fastavro-1.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:861efe9ad25fc26c3d360761d48930e6aad0cabe5ae888f92a721699bfe612ed"}, + {file = "fastavro-1.8.3.tar.gz", hash = "sha256:a6c2ec69516e908fce64d93a13e6e83afb880f2edb5ad3adaa1eb04c918de6d8"}, ] [package.extras] @@ -1696,21 +1678,19 @@ zstandard = ["zstandard"] [[package]] name = "filelock" -version = "3.12.3" +version = "3.12.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.12.3-py3-none-any.whl", hash = "sha256:f067e40ccc40f2b48395a80fcbd4728262fab54e232e090a4063ab804179efeb"}, - {file = "filelock-3.12.3.tar.gz", hash = "sha256:0ecc1dd2ec4672a10c8550a8182f1bd0c0a5088470ecd5a125e45f49472fac3d"}, + {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, + {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.11\""} - [package.extras] docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] +typing = ["typing-extensions (>=4.7.1)"] [[package]] name = "filetype" @@ -1874,13 +1854,13 @@ files = [ [[package]] name = "fsspec" -version = "2023.9.0" +version = "2023.9.1" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2023.9.0-py3-none-any.whl", hash = "sha256:d55b9ab2a4c1f2b759888ae9f93e40c2aa72c0808132e87e282b549f9e6c4254"}, - {file = "fsspec-2023.9.0.tar.gz", hash = "sha256:4dbf0fefee035b7c6d3bbbe6bc99b2f201f40d4dca95b67c2b719be77bcd917f"}, + {file = "fsspec-2023.9.1-py3-none-any.whl", hash = "sha256:99a974063b6cced36cfaa61aa8efb05439c6fea2dafe65930e7ab46f9d2f8930"}, + {file = "fsspec-2023.9.1.tar.gz", hash = "sha256:da8cfe39eeb65aaa69074d5e0e4bbc9b7ef72d69c0587a31cab981eefdb3da13"}, ] [package.extras] @@ -1909,51 +1889,56 @@ tqdm = ["tqdm"] [[package]] name = "gevent" -version = "23.9.0.post1" +version = "23.9.1" description = "Coroutine-based network library" optional = false python-versions = ">=3.8" files = [ - {file = "gevent-23.9.0.post1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c4b2efc68fb3aef5dde8204d0f71c3585ba621c57e9b937b46ff5678f1cd7404"}, - {file = "gevent-23.9.0.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b3a813ff1151d75538bb5ec821332627cd2c4685cc72702640d203a426041ca"}, - {file = "gevent-23.9.0.post1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf108ee9c18c0ea5cf81d3fc7859f512dab61c2d76937b2510c7bf8cfaabfe7"}, - {file = "gevent-23.9.0.post1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ff1771bc8f2ed343f32c2f40dbd25f04fdfe2d83eb02e0401945dc61115dbe"}, - {file = "gevent-23.9.0.post1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26e308815fb2d4d84e7a55eebd00c4014e5cb07ead8f3f66236e5a797937340c"}, - {file = "gevent-23.9.0.post1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fd8941f5c5cc998114b89e032e1ebabd779d99faa60d004b960587b866195ba"}, - {file = "gevent-23.9.0.post1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:deb353bf15ab724fe8bf587433519d558ddfd89fa35b77f7886de4312517eee4"}, - {file = "gevent-23.9.0.post1-cp310-cp310-win_amd64.whl", hash = "sha256:9a4c1afd3fa2103f11c27f19b060c2ed122ed487cbdf79e7987ef261aa04429f"}, - {file = "gevent-23.9.0.post1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:29ccc476077a317d082ddad4dabf5c68ccf7079aaf14aa5be8e0529b06f569a6"}, - {file = "gevent-23.9.0.post1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cb909b0649b0e15c069527a61af83f067e4c59ff03a07aa40aa2d5e8e355d20"}, - {file = "gevent-23.9.0.post1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f463a131df0e8d466a8caf7909ad73c80f793ed97c6376e78c7c75a51f19cba0"}, - {file = "gevent-23.9.0.post1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:edb9ceb5f88154e83ee8fc2e4b2d8ca070c62f1266d73f88578109b9c4564003"}, - {file = "gevent-23.9.0.post1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ee6382fde487a84a4a21711988d9eb97ed63c69be085b442e1665dc44022be60"}, - {file = "gevent-23.9.0.post1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9d21796a54dcccabe9fc0053c1bd991dfa63e554873e5a5f9c0885984068b2a"}, - {file = "gevent-23.9.0.post1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d33f997d97f267e9f62db9cd03d42f711df2ddba944173853773b220187ca7a0"}, - {file = "gevent-23.9.0.post1-cp311-cp311-win_amd64.whl", hash = "sha256:4bdca1bd1fb0c3524dbe0a273c87eb9a0428ea7f2533d579a3194426fbb93c92"}, - {file = "gevent-23.9.0.post1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:bccd4e3d21e7c5f7b72e3382523702ce58add691417633dfafa305978bebee84"}, - {file = "gevent-23.9.0.post1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c24bd27f8a75fe70475e72dde519d569d58f0f5e8f4f6d009493ee660855c3d1"}, - {file = "gevent-23.9.0.post1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc5b637870c325899eb9fc44915670deb2ef413c5c90ad0d96c335e41de1f751"}, - {file = "gevent-23.9.0.post1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bcff1fc4bc0e5610aa541ad14fead244e8b789fda98acbacd268668089c7373"}, - {file = "gevent-23.9.0.post1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c3d665d252903982469b0933f31dd346a249d2e2c45dd0e1c9263889a5dbfbc6"}, - {file = "gevent-23.9.0.post1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f23a560f1731a2b4f582b89e8d8afcbfd66695b025712e295f21aeec3d786413"}, - {file = "gevent-23.9.0.post1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b2804d7e2909074b0cf6e2371595935a699edc8bd403211a414752e68f7e0ad"}, - {file = "gevent-23.9.0.post1-cp312-cp312-win_amd64.whl", hash = "sha256:f7aa27b8585b66fb5fff3a54e3e7bb837258bda39bb65a788304c8d45b9bb9d4"}, - {file = "gevent-23.9.0.post1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:bc836d91b834fa4ce18ee062861dc6e488f35254def8301ffcac6900331941a7"}, - {file = "gevent-23.9.0.post1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a21b9c7356e9ab0baaa8afa85fb18406cbff54d3cf8033e1e97e7186a3deb391"}, - {file = "gevent-23.9.0.post1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3c4acda344e9864b2d0755fad1c736dc4effae95b0fd8915a261ff6ace09416f"}, - {file = "gevent-23.9.0.post1-cp38-cp38-win32.whl", hash = "sha256:22d7fdbfc7127c5d59511c3de9f8394a125f32bccc1254915944d95522876a8e"}, - {file = "gevent-23.9.0.post1-cp38-cp38-win_amd64.whl", hash = "sha256:3e6b6c53e1e81b3f22180da316769ac55a41085655971e0e086899f0ddb017b0"}, - {file = "gevent-23.9.0.post1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:f0dbee943865313331ece9f9675a30848d027df653b0ff4881d2be14d0c2ea1c"}, - {file = "gevent-23.9.0.post1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:98de0f1eecd772df87018e04ef8e274b72c3b3127d2e15f76b8b761ed135b803"}, - {file = "gevent-23.9.0.post1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17ebb6f981389c17321b95bc59ff6a65edeb98f3205884babaec9cb514aaa0d3"}, - {file = "gevent-23.9.0.post1-cp39-cp39-win32.whl", hash = "sha256:f731574d908cbe505e103f4c5b4d64fe4e0a82cef371e925212689194ee22198"}, - {file = "gevent-23.9.0.post1-cp39-cp39-win_amd64.whl", hash = "sha256:595706422f1832f2dd29bb9cb3219780f1e158d5a771199fe26b00da1bae8214"}, - {file = "gevent-23.9.0.post1.tar.gz", hash = "sha256:943f26edada39dfd5f50551157bb9011191c7367be36e341d0f1cdecfe07a229"}, + {file = "gevent-23.9.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39"}, + {file = "gevent-23.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae"}, + {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6"}, + {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7"}, + {file = "gevent-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e"}, + {file = "gevent-23.9.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011"}, + {file = "gevent-23.9.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7"}, + {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71"}, + {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e"}, + {file = "gevent-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a"}, + {file = "gevent-23.9.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea"}, + {file = "gevent-23.9.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599"}, + {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303"}, + {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d"}, + {file = "gevent-23.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1"}, + {file = "gevent-23.9.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe"}, + {file = "gevent-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5"}, + {file = "gevent-23.9.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397"}, + {file = "gevent-23.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507"}, + {file = "gevent-23.9.1-cp38-cp38-win32.whl", hash = "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a"}, + {file = "gevent-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f"}, + {file = "gevent-23.9.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a"}, + {file = "gevent-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653"}, + {file = "gevent-23.9.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd"}, + {file = "gevent-23.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543"}, + {file = "gevent-23.9.1-cp39-cp39-win32.whl", hash = "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2"}, + {file = "gevent-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b"}, + {file = "gevent-23.9.1.tar.gz", hash = "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34"}, ] [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""} +greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} "zope.event" = "*" "zope.interface" = "*" @@ -2103,18 +2088,21 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.34" +version = "3.1.36" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.34-py3-none-any.whl", hash = "sha256:5d3802b98a3bae1c2b8ae0e1ff2e4aa16bcdf02c145da34d092324f599f01395"}, - {file = "GitPython-3.1.34.tar.gz", hash = "sha256:85f7d365d1f6bf677ae51039c1ef67ca59091c7ebd5a3509aa399d4eda02d6dd"}, + {file = "GitPython-3.1.36-py3-none-any.whl", hash = "sha256:8d22b5cfefd17c79914226982bb7851d6ade47545b1735a9d010a2a4c26d8388"}, + {file = "GitPython-3.1.36.tar.gz", hash = "sha256:4bb0c2a6995e85064140d31a33289aa5dce80133a23d36fcd372d716c54d3ebf"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" +[package.extras] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-sugar", "virtualenv"] + [[package]] name = "google-api-core" version = "2.11.1" @@ -2141,13 +2129,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.97.0" +version = "2.99.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-python-client-2.97.0.tar.gz", hash = "sha256:48277291894876a1ca7ed4127e055e81f81e6343ced1b544a7200ae2c119dcd7"}, - {file = "google_api_python_client-2.97.0-py2.py3-none-any.whl", hash = "sha256:5215f4cd577753fc4192ccfbe0bb8b55d4bb5fd68fa6268ac5cf271b6305de31"}, + {file = "google-api-python-client-2.99.0.tar.gz", hash = "sha256:e733fd0f2c8793b1a000d5e69ac81b1b9ec0665b445b7ed83bdbbb0038973306"}, + {file = "google_api_python_client-2.99.0-py2.py3-none-any.whl", hash = "sha256:40272131d3a4a7aecab840ebcf3df51c54d49560156f3b9d54a4ef82c795985d"}, ] [package.dependencies] @@ -2159,20 +2147,19 @@ uritemplate = ">=3.0.1,<5" [[package]] name = "google-auth" -version = "2.22.0" +version = "2.23.0" description = "Google Authentication Library" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "google-auth-2.22.0.tar.gz", hash = "sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce"}, - {file = "google_auth-2.22.0-py2.py3-none-any.whl", hash = "sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873"}, + {file = "google-auth-2.23.0.tar.gz", hash = "sha256:753a26312e6f1eaeec20bc6f2644a10926697da93446e1f8e24d6d32d45a922a"}, + {file = "google_auth-2.23.0-py2.py3-none-any.whl", hash = "sha256:2cec41407bd1e207f5b802638e32bb837df968bb5c05f413d0fa526fac4cf7a7"}, ] [package.dependencies] cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" rsa = ">=3.1.4,<5" -six = ">=1.9.0" urllib3 = "<2.0" [package.extras] @@ -2184,29 +2171,28 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-auth-httplib2" -version = "0.1.0" +version = "0.1.1" description = "Google Authentication Library: httplib2 transport" optional = false python-versions = "*" files = [ - {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, - {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, + {file = "google-auth-httplib2-0.1.1.tar.gz", hash = "sha256:c64bc555fdc6dd788ea62ecf7bccffcf497bf77244887a3f3d7a5a02f8e3fc29"}, + {file = "google_auth_httplib2-0.1.1-py2.py3-none-any.whl", hash = "sha256:42c50900b8e4dcdf8222364d1f0efe32b8421fb6ed72f2613f12f75cc933478c"}, ] [package.dependencies] google-auth = "*" -httplib2 = ">=0.15.0" -six = "*" +httplib2 = ">=0.19.0" [[package]] name = "google-cloud-aiplatform" -version = "1.31.1" +version = "1.32.0" description = "Vertex AI API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-aiplatform-1.31.1.tar.gz", hash = "sha256:6de8d7d647990cc0ee601d938d3a1693e3ef50f3d54d735397b2e31ca8eeb946"}, - {file = "google_cloud_aiplatform-1.31.1-py2.py3-none-any.whl", hash = "sha256:360d95c4c6f6a27fc2a4a071741a66588f0f0ca245509315839cfa320d6862e2"}, + {file = "google-cloud-aiplatform-1.32.0.tar.gz", hash = "sha256:18558f2a908a86a9b78b744486c71dcf0c836b232466422277b2da572d30b8f4"}, + {file = "google_cloud_aiplatform-1.32.0-py2.py3-none-any.whl", hash = "sha256:18cf609aadb1070cb3831370cdd7bcecc87ec07b84a5fa26b5df22a592d4b002"}, ] [package.dependencies] @@ -2224,14 +2210,15 @@ autologging = ["mlflow (>=1.27.0,<=2.1.1)"] cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] datasets = ["pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)"] endpoint = ["requests (>=2.28.1)"] -full = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "google-vizier (>=0.1.6)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (==5.3.1)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +full = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (==0.0.4)", "google-vizier (>=0.1.6)", "importlib-metadata (<7.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyyaml (==5.3.1)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] pipelines = ["pyyaml (==5.3.1)"] prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.76.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] +preview = ["cloudpickle (<3.0)", "google-cloud-logging (<4.0)", "importlib-metadata (<7.0)"] private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] tensorboard = ["tensorflow (>=2.3.0,<3.0.0dev)"] -testing = ["docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-vizier (==0.0.4)", "google-vizier (>=0.1.6)", "grpcio-testing", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pytest-asyncio", "pytest-xdist", "pyyaml (==5.3.1)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +testing = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.76.0)", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (==0.0.4)", "google-vizier (>=0.1.6)", "grpcio-testing", "importlib-metadata (<7.0)", "ipython", "kfp", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (==5.3.1)", "requests (>=2.28.1)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<2.13.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch", "torch (>=2.0.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] vizier = ["google-vizier (==0.0.4)", "google-vizier (>=0.1.6)"] xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] @@ -2405,20 +2392,20 @@ testing = ["pytest"] [[package]] name = "google-resumable-media" -version = "2.5.0" +version = "2.6.0" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false python-versions = ">= 3.7" files = [ - {file = "google-resumable-media-2.5.0.tar.gz", hash = "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93"}, - {file = "google_resumable_media-2.5.0-py2.py3-none-any.whl", hash = "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec"}, + {file = "google-resumable-media-2.6.0.tar.gz", hash = "sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7"}, + {file = "google_resumable_media-2.6.0-py2.py3-none-any.whl", hash = "sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b"}, ] [package.dependencies] google-crc32c = ">=1.0,<2.0dev" [package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "google-auth (>=1.22.0,<2.0dev)"] requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] @@ -2454,13 +2441,13 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "gotrue" -version = "1.0.4" +version = "1.1.0" description = "Python Client Library for GoTrue" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "gotrue-1.0.4-py3-none-any.whl", hash = "sha256:f016f5e317a21e55dfcee00fb360f2c7a33c5b87a38601e7b9e65cb898bcf7bb"}, - {file = "gotrue-1.0.4.tar.gz", hash = "sha256:2686c93b798fb2d3b120f067d21e66bb803b013ee6ab6fb7783093102f74603a"}, + {file = "gotrue-1.1.0-py3-none-any.whl", hash = "sha256:ce60638cf56f07cc01580f63ade1299d7a2bbc636c48b0f3a026d8f265d982b4"}, + {file = "gotrue-1.1.0.tar.gz", hash = "sha256:7954683d140a58fb906b8eb8f555ac39a888ba985f9bffe481d12ac1437ad46a"}, ] [package.dependencies] @@ -3548,13 +3535,13 @@ test = ["psutil", "pytest", "pytest-asyncio"] [[package]] name = "langsmith" -version = "0.0.33" +version = "0.0.37" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langsmith-0.0.33-py3-none-any.whl", hash = "sha256:cdff11a6272d3cba72c151960c0319b1d36e0770d37f05061d6c31ef1a2404a4"}, - {file = "langsmith-0.0.33.tar.gz", hash = "sha256:c9c640ac238d4cabc8f9744e04346d3dfaf0ca6c9dc37bd2a25b8031eda35dc3"}, + {file = "langsmith-0.0.37-py3-none-any.whl", hash = "sha256:bdfec3664162e672f89f9e4d82cbbd3f32587295d3064aab1746080a873ac3a0"}, + {file = "langsmith-0.0.37.tar.gz", hash = "sha256:a20e105329cae9a588414443b0a6eb56c776187e3aab47c327848bae0f1a4377"}, ] [package.dependencies] @@ -3583,21 +3570,21 @@ test = ["coverage", "pytest", "pytest-cov"] [[package]] name = "llama-cpp-python" -version = "0.1.83" +version = "0.1.85" description = "A Python wrapper for llama.cpp" optional = true python-versions = ">=3.7" files = [ - {file = "llama_cpp_python-0.1.83.tar.gz", hash = "sha256:9f40656e46a85a3c3427790246e03490bb90202c37cb99732a095ffcb99efe54"}, + {file = "llama_cpp_python-0.1.85.tar.gz", hash = "sha256:9ad2269f47a5fac10e78565e0b4078ea6b8d56ddd3b78892967da4739684db2b"}, ] [package.dependencies] -diskcache = ">=5.6.1,<6.0.0" -numpy = ">=1.24.4,<2.0.0" -typing-extensions = ">=4.7.1,<5.0.0" +diskcache = ">=5.6.1" +numpy = ">=1.20.0" +typing-extensions = ">=4.5.0" [package.extras] -server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (>=1.6.1)", "uvicorn (>=0.23.2,<0.24.0)"] +server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (>=1.6.1)", "uvicorn (>=0.22.0)"] [[package]] name = "locust" @@ -3628,13 +3615,13 @@ Werkzeug = ">=2.0.0" [[package]] name = "loguru" -version = "0.7.1" +version = "0.7.2" description = "Python logging made (stupidly) simple" optional = false python-versions = ">=3.5" files = [ - {file = "loguru-0.7.1-py3-none-any.whl", hash = "sha256:046bf970cb3cad77a28d607cbf042ac25a407db987a1e801c7f7e692469982f9"}, - {file = "loguru-0.7.1.tar.gz", hash = "sha256:7ba2a7d81b79a412b0ded69bd921e012335e80fd39937a633570f273a343579e"}, + {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, + {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, ] [package.dependencies] @@ -3642,7 +3629,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "pre-commit (==3.3.1)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] +dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] [[package]] name = "lxml" @@ -4314,6 +4301,31 @@ doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx- extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] +[[package]] +name = "nh3" +version = "0.2.14" +description = "Ammonia HTML sanitizer Python binding" +optional = false +python-versions = "*" +files = [ + {file = "nh3-0.2.14-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9be2f68fb9a40d8440cbf34cbf40758aa7f6093160bfc7fb018cce8e424f0c3a"}, + {file = "nh3-0.2.14-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f99212a81c62b5f22f9e7c3e347aa00491114a5647e1f13bbebd79c3e5f08d75"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7771d43222b639a4cd9e341f870cee336b9d886de1ad9bec8dddab22fe1de450"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:525846c56c2bcd376f5eaee76063ebf33cf1e620c1498b2a40107f60cfc6054e"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e8986f1dd3221d1e741fda0a12eaa4a273f1d80a35e31a1ffe579e7c621d069e"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18415df36db9b001f71a42a3a5395db79cf23d556996090d293764436e98e8ad"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:377aaf6a9e7c63962f367158d808c6a1344e2b4f83d071c43fbd631b75c4f0b2"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0be5c792bd43d0abef8ca39dd8acb3c0611052ce466d0401d51ea0d9aa7525"}, + {file = "nh3-0.2.14-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:93a943cfd3e33bd03f77b97baa11990148687877b74193bf777956b67054dcc6"}, + {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ac8056e937f264995a82bf0053ca898a1cb1c9efc7cd68fa07fe0060734df7e4"}, + {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:203cac86e313cf6486704d0ec620a992c8bc164c86d3a4fd3d761dd552d839b5"}, + {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:5529a3bf99402c34056576d80ae5547123f1078da76aa99e8ed79e44fa67282d"}, + {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:aed56a86daa43966dd790ba86d4b810b219f75b4bb737461b6886ce2bde38fd6"}, + {file = "nh3-0.2.14-cp37-abi3-win32.whl", hash = "sha256:116c9515937f94f0057ef50ebcbcc10600860065953ba56f14473ff706371873"}, + {file = "nh3-0.2.14-cp37-abi3-win_amd64.whl", hash = "sha256:88c753efbcdfc2644a5012938c6b9753f1c64a5723a67f0301ca43e7b85dcf0e"}, + {file = "nh3-0.2.14.tar.gz", hash = "sha256:a0c509894fd4dccdff557068e5074999ae3b75f4c5a2d6fb5415e782e25679c4"}, +] + [[package]] name = "nltk" version = "3.8.1" @@ -4341,41 +4353,41 @@ twitter = ["twython"] [[package]] name = "numexpr" -version = "2.8.5" +version = "2.8.6" description = "Fast numerical expression evaluator for NumPy" optional = false python-versions = ">=3.7" files = [ - {file = "numexpr-2.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51f3ab160c3847ebcca93cd88f935a7802b54a01ab63fe93152994a64d7a6cf2"}, - {file = "numexpr-2.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:de29c77f674e4eb8f0846525a475cab64008c227c8bc4ba5153ab3f72441cc63"}, - {file = "numexpr-2.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf85ba1327eb87ec82ae7936f13c8850fb969a0ca34f3ba9fa3897c09d5c80d7"}, - {file = "numexpr-2.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c00be69f747f44a631830215cab482f0f77f75af2925695adff57c1cc0f9a68"}, - {file = "numexpr-2.8.5-cp310-cp310-win32.whl", hash = "sha256:c46350dcdb93e32f033eea5a21269514ffcaf501d9abd6036992d37e48a308b0"}, - {file = "numexpr-2.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:894b027438b8ec88dea32a19193716c79f4ff8ddb92302dcc9731b51ba3565a8"}, - {file = "numexpr-2.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6df184d40d4cf9f21c71f429962f39332f7398147762588c9f3a5c77065d0c06"}, - {file = "numexpr-2.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:178b85ad373c6903e55d75787d61b92380439b70d94b001cb055a501b0821335"}, - {file = "numexpr-2.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:578fe4008e4d5d6ff01bbeb2d7b7ba1ec658a5cda9c720cd26a9a8325f8ef438"}, - {file = "numexpr-2.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef621b4ee366a5c6a484f6678c9259f5b826569f8bfa0b89ba2306d5055468bb"}, - {file = "numexpr-2.8.5-cp311-cp311-win32.whl", hash = "sha256:dd57ab1a3d3aaa9274aff1cefbf93b8ddacc7973afef5b125905f6bf18fabab0"}, - {file = "numexpr-2.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:783324ba40eb804ecfc9ebae86120a1e339ab112d0ab8a1f0d48a26354d5bf9b"}, - {file = "numexpr-2.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:183d5430db76826e54465c69db93a3c6ecbf03cda5aa1bb96eaad0147e9b68dc"}, - {file = "numexpr-2.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39ce106f92ccea5b07b1d6f2f3c4370f05edf27691dc720a63903484a2137e48"}, - {file = "numexpr-2.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b594dc9e2d6291a0bc5c065e6d9caf3eee743b5663897832e9b17753c002947a"}, - {file = "numexpr-2.8.5-cp37-cp37m-win32.whl", hash = "sha256:62b4faf8e0627673b0210a837792bddd23050ecebc98069ab23eb0633ff1ef5f"}, - {file = "numexpr-2.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:db5c65417d69414f1ab31302ea01d3548303ef31209c38b4849d145be4e1d1ba"}, - {file = "numexpr-2.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb36ffcfa1606e41aa08d559b4277bcad0e16b83941d1a4fee8d2bd5a34f8e0e"}, - {file = "numexpr-2.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:34af2a0e857d02a4bc5758bc037a777d50dacb13bcd57c7905268a3e44994ed6"}, - {file = "numexpr-2.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a8dad2bfaad5a5c34a2e8bbf62b9df1dfab266d345fda1feb20ff4e264b347a"}, - {file = "numexpr-2.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93f5a866cd13a808bc3d3a9c487d94cd02eec408b275ff0aa150f2e8e5191f8"}, - {file = "numexpr-2.8.5-cp38-cp38-win32.whl", hash = "sha256:558390fea6370003ac749ed9d0f38d708aa096f5dcb707ddb6e0ca5a0dd37da1"}, - {file = "numexpr-2.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:55983806815035eb63c5039520688c49536bb7f3cc3fc1d7d64c6a00cf3f353e"}, - {file = "numexpr-2.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1510da20e6f5f45333610b1ded44c566e2690c6c437c84f2a212ca09627c7e01"}, - {file = "numexpr-2.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e8b5bf7bcb4e8dcd66522d8fc96e1db7278f901cb4fd2e155efbe62a41dde08"}, - {file = "numexpr-2.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ed0e1c1ef5f34381448539f1fe9015906d21c9cfa2797c06194d4207dadb465"}, - {file = "numexpr-2.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aea6ab45c87c0a7041183c08a798f0ad4d7c5eccbce20cfe79ce6f1a45ef3702"}, - {file = "numexpr-2.8.5-cp39-cp39-win32.whl", hash = "sha256:cbfd833ee5fdb0efb862e152aee7e6ccea9c596d5c11d22604c2e6307bff7cad"}, - {file = "numexpr-2.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:283ce8609a7ccbadf91a68f3484558b3e36d27c93c98a41ec205efb0ab43c872"}, - {file = "numexpr-2.8.5.tar.gz", hash = "sha256:45ed41e55a0abcecf3d711481e12a5fb7a904fe99d42bc282a17cc5f8ea510be"}, + {file = "numexpr-2.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80acbfefb68bd92e708e09f0a02b29e04d388b9ae72f9fcd57988aca172a7833"}, + {file = "numexpr-2.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e884687da8af5955dc9beb6a12d469675c90b8fb38b6c93668c989cfc2cd982"}, + {file = "numexpr-2.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ef7e8aaa84fce3aba2e65f243d14a9f8cc92aafd5d90d67283815febfe43eeb"}, + {file = "numexpr-2.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee04d72307c09599f786b9231acffb10df7d7a74b2ce3681d74a574880d13ce"}, + {file = "numexpr-2.8.6-cp310-cp310-win32.whl", hash = "sha256:211804ec25a9f6d188eadf4198dd1a92b2f61d7d20993c6c7706139bc4199c5b"}, + {file = "numexpr-2.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:18b1804923cfa3be7bbb45187d01c0540c8f6df4928c22a0f786e15568e9ebc5"}, + {file = "numexpr-2.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95b9da613761e4fc79748535b2a1f58cada22500e22713ae7d9571fa88d1c2e2"}, + {file = "numexpr-2.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47b45da5aa25600081a649f5e8b2aa640e35db3703f4631f34bb1f2f86d1b5b4"}, + {file = "numexpr-2.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84979bf14143351c2db8d9dd7fef8aca027c66ad9df9cb5e75c93bf5f7b5a338"}, + {file = "numexpr-2.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d36528a33aa9c23743b3ea686e57526a4f71e7128a1be66210e1511b09c4e4e9"}, + {file = "numexpr-2.8.6-cp311-cp311-win32.whl", hash = "sha256:681812e2e71ff1ba9145fac42d03f51ddf6ba911259aa83041323f68e7458002"}, + {file = "numexpr-2.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:27782177a0081bd0aab229be5d37674e7f0ab4264ef576697323dd047432a4cd"}, + {file = "numexpr-2.8.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ef6e8896457a60a539cb6ba27da78315a9bb31edb246829b25b5b0304bfcee91"}, + {file = "numexpr-2.8.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e640bc0eaf1b59f3dde52bc02bbfda98e62f9950202b0584deba28baf9f36bbb"}, + {file = "numexpr-2.8.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d126938c2c3784673c9c58d94e00b1570aa65517d9c33662234d442fc9fb5795"}, + {file = "numexpr-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:e93d64cd20940b726477c3cb64926e683d31b778a1e18f9079a5088fd0d8e7c8"}, + {file = "numexpr-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:31cf610c952eec57081171f0b4427f9bed2395ec70ec432bbf45d260c5c0cdeb"}, + {file = "numexpr-2.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5f96c89aa0b1f13685ec32fa3d71028db0b5981bfd99a0bbc271035949136b3"}, + {file = "numexpr-2.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c8f37f7a6af3bdd61f2efd1cafcc083a9525ab0aaf5dc641e7ec8fc0ae2d3aa1"}, + {file = "numexpr-2.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38b8b90967026bbc36c7aa6e8ca3b8906e1990914fd21f446e2a043f4ee3bc06"}, + {file = "numexpr-2.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1967c16f61c27df1cdc43ba3c0ba30346157048dd420b4259832276144d0f64e"}, + {file = "numexpr-2.8.6-cp38-cp38-win32.whl", hash = "sha256:15469dc722b5ceb92324ec8635411355ebc702303db901ae8cc87f47c5e3a124"}, + {file = "numexpr-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:95c09e814b0d6549de98b5ded7cdf7d954d934bb6b505432ff82e83a6d330bda"}, + {file = "numexpr-2.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aa0f661f5f4872fd7350cc9895f5d2594794b2a7e7f1961649a351724c64acc9"}, + {file = "numexpr-2.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8e3e6f1588d6c03877cb3b3dcc3096482da9d330013b886b29cb9586af5af3eb"}, + {file = "numexpr-2.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8564186aad5a2c88d597ebc79b8171b52fd33e9b085013e1ff2208f7e4b387e3"}, + {file = "numexpr-2.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6a88d71c166e86b98d34701285d23e3e89d548d9f5ae3f4b60919ac7151949f"}, + {file = "numexpr-2.8.6-cp39-cp39-win32.whl", hash = "sha256:c48221b6a85494a7be5a022899764e58259af585dff031cecab337277278cc93"}, + {file = "numexpr-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:6d7003497d82ef19458dce380b36a99343b96a3bd5773465c2d898bf8f5a38f9"}, + {file = "numexpr-2.8.6.tar.gz", hash = "sha256:6336f8dba3f456e41a4ffc3c97eb63d89c73589ff6e1707141224b930263260d"}, ] [package.dependencies] @@ -4383,36 +4395,43 @@ numpy = ">=1.13.3" [[package]] name = "numpy" -version = "1.25.2" +version = "1.26.0" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = "<3.13,>=3.9" files = [ - {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, - {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, - {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, - {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, - {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, - {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, - {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, - {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, - {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, - {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, - {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, - {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, - {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, - {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, - {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, - {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, - {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, - {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, - {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, - {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, - {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, - {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, - {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, - {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, - {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, + {file = "numpy-1.26.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8db2f125746e44dce707dd44d4f4efeea8d7e2b43aace3f8d1f235cfa2733dd"}, + {file = "numpy-1.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0621f7daf973d34d18b4e4bafb210bbaf1ef5e0100b5fa750bd9cde84c7ac292"}, + {file = "numpy-1.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51be5f8c349fdd1a5568e72713a21f518e7d6707bcf8503b528b88d33b57dc68"}, + {file = "numpy-1.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:767254ad364991ccfc4d81b8152912e53e103ec192d1bb4ea6b1f5a7117040be"}, + {file = "numpy-1.26.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:436c8e9a4bdeeee84e3e59614d38c3dbd3235838a877af8c211cfcac8a80b8d3"}, + {file = "numpy-1.26.0-cp310-cp310-win32.whl", hash = "sha256:c2e698cb0c6dda9372ea98a0344245ee65bdc1c9dd939cceed6bb91256837896"}, + {file = "numpy-1.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:09aaee96c2cbdea95de76ecb8a586cb687d281c881f5f17bfc0fb7f5890f6b91"}, + {file = "numpy-1.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:637c58b468a69869258b8ae26f4a4c6ff8abffd4a8334c830ffb63e0feefe99a"}, + {file = "numpy-1.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:306545e234503a24fe9ae95ebf84d25cba1fdc27db971aa2d9f1ab6bba19a9dd"}, + {file = "numpy-1.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6adc33561bd1d46f81131d5352348350fc23df4d742bb246cdfca606ea1208"}, + {file = "numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e062aa24638bb5018b7841977c360d2f5917268d125c833a686b7cbabbec496c"}, + {file = "numpy-1.26.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:546b7dd7e22f3c6861463bebb000646fa730e55df5ee4a0224408b5694cc6148"}, + {file = "numpy-1.26.0-cp311-cp311-win32.whl", hash = "sha256:c0b45c8b65b79337dee5134d038346d30e109e9e2e9d43464a2970e5c0e93229"}, + {file = "numpy-1.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:eae430ecf5794cb7ae7fa3808740b015aa80747e5266153128ef055975a72b99"}, + {file = "numpy-1.26.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:166b36197e9debc4e384e9c652ba60c0bacc216d0fc89e78f973a9760b503388"}, + {file = "numpy-1.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f042f66d0b4ae6d48e70e28d487376204d3cbf43b84c03bac57e28dac6151581"}, + {file = "numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e18e5b14a7560d8acf1c596688f4dfd19b4f2945b245a71e5af4ddb7422feb"}, + {file = "numpy-1.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6bad22a791226d0a5c7c27a80a20e11cfe09ad5ef9084d4d3fc4a299cca505"}, + {file = "numpy-1.26.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4acc65dd65da28060e206c8f27a573455ed724e6179941edb19f97e58161bb69"}, + {file = "numpy-1.26.0-cp312-cp312-win32.whl", hash = "sha256:bb0d9a1aaf5f1cb7967320e80690a1d7ff69f1d47ebc5a9bea013e3a21faec95"}, + {file = "numpy-1.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee84ca3c58fe48b8ddafdeb1db87388dce2c3c3f701bf447b05e4cfcc3679112"}, + {file = "numpy-1.26.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a873a8180479bc829313e8d9798d5234dfacfc2e8a7ac188418189bb8eafbd2"}, + {file = "numpy-1.26.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:914b28d3215e0c721dc75db3ad6d62f51f630cb0c277e6b3bcb39519bed10bd8"}, + {file = "numpy-1.26.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c78a22e95182fb2e7874712433eaa610478a3caf86f28c621708d35fa4fd6e7f"}, + {file = "numpy-1.26.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f737708b366c36b76e953c46ba5827d8c27b7a8c9d0f471810728e5a2fe57c"}, + {file = "numpy-1.26.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b44e6a09afc12952a7d2a58ca0a2429ee0d49a4f89d83a0a11052da696440e49"}, + {file = "numpy-1.26.0-cp39-cp39-win32.whl", hash = "sha256:5671338034b820c8d58c81ad1dafc0ed5a00771a82fccc71d6438df00302094b"}, + {file = "numpy-1.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:020cdbee66ed46b671429c7265cf00d8ac91c046901c55684954c3958525dab2"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0792824ce2f7ea0c82ed2e4fecc29bb86bee0567a080dacaf2e0a01fe7654369"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d484292eaeb3e84a51432a94f53578689ffdea3f90e10c8b203a99be5af57d8"}, + {file = "numpy-1.26.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:186ba67fad3c60dbe8a3abff3b67a91351100f2661c8e2a80364ae6279720299"}, + {file = "numpy-1.26.0.tar.gz", hash = "sha256:f93fc78fe8bf15afe2b8d6b6499f1c73953169fad1e9a8dd086cdff3190e7fdf"}, ] [[package]] @@ -4504,13 +4523,13 @@ et-xmlfile = "*" [[package]] name = "opentelemetry-api" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_api-1.19.0-py3-none-any.whl", hash = "sha256:dcd2a0ad34b691964947e1d50f9e8c415c32827a1d87f0459a72deb9afdf5597"}, - {file = "opentelemetry_api-1.19.0.tar.gz", hash = "sha256:db374fb5bea00f3c7aa290f5d94cea50b659e6ea9343384c5f6c2bb5d5e8db65"}, + {file = "opentelemetry_api-1.20.0-py3-none-any.whl", hash = "sha256:982b76036fec0fdaf490ae3dfd9f28c81442a33414f737abc687a32758cdcba5"}, + {file = "opentelemetry_api-1.20.0.tar.gz", hash = "sha256:06abe351db7572f8afdd0fb889ce53f3c992dbf6f6262507b385cc1963e06983"}, ] [package.dependencies] @@ -4519,42 +4538,43 @@ importlib-metadata = ">=6.0,<7.0" [[package]] name = "opentelemetry-exporter-otlp" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Collector Exporters" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_exporter_otlp-1.19.0-py3-none-any.whl", hash = "sha256:5316ffc754d5a4fb4faaa811a837593edd680bed429fd7c10d898e45f4946903"}, - {file = "opentelemetry_exporter_otlp-1.19.0.tar.gz", hash = "sha256:2d4b066180452b8e74a0a598d98901769148a0fe17900c70aca37d3c7c4e8aaa"}, + {file = "opentelemetry_exporter_otlp-1.20.0-py3-none-any.whl", hash = "sha256:3b4d47726da83fef84467bdf96da4f8f3d1a61b35db3c16354c391ce8e9decf6"}, + {file = "opentelemetry_exporter_otlp-1.20.0.tar.gz", hash = "sha256:f8cb69f80c333166e5cfaa030f9e28f7faaf343aff24caaa2cb4202ea4849b6b"}, ] [package.dependencies] -opentelemetry-exporter-otlp-proto-grpc = "1.19.0" -opentelemetry-exporter-otlp-proto-http = "1.19.0" +opentelemetry-exporter-otlp-proto-grpc = "1.20.0" +opentelemetry-exporter-otlp-proto-http = "1.20.0" [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Protobuf encoding" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_exporter_otlp_proto_common-1.19.0-py3-none-any.whl", hash = "sha256:792d5496ecfebaf4f56b2c434c5e2823f88ffaeb5dcd272ea423b249fd52bded"}, - {file = "opentelemetry_exporter_otlp_proto_common-1.19.0.tar.gz", hash = "sha256:c13d02a31dec161f8910d96db6b58309af17d92b827c64284bf85eec3f2d7297"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.20.0-py3-none-any.whl", hash = "sha256:dd63209b40702636ab6ae76a06b401b646ad7b008a906ecb41222d4af24fbdef"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.20.0.tar.gz", hash = "sha256:df60c681bd61812e50b3a39a7a1afeeb6d4066117583249fcc262269374e7a49"}, ] [package.dependencies] -opentelemetry-proto = "1.19.0" +backoff = {version = ">=1.10.0,<3.0.0", markers = "python_version >= \"3.7\""} +opentelemetry-proto = "1.20.0" [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_exporter_otlp_proto_grpc-1.19.0-py3-none-any.whl", hash = "sha256:ae2a6484d12ba4d0f1096c4565193c1c27a951a9d2b10a9a84da3e1f866e84d6"}, - {file = "opentelemetry_exporter_otlp_proto_grpc-1.19.0.tar.gz", hash = "sha256:e69261b4da8cbaa42d9b5f1cff4fcebbf8a3c02f85d69a8aea698312084f4180"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.20.0-py3-none-any.whl", hash = "sha256:7c3f066065891b56348ba2c7f9df6ec635a712841cae0a36f2f6a81642ae7dec"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.20.0.tar.gz", hash = "sha256:6c06d43c3771bda1795226e327722b4b980fa1ca1ec9e985f2ef3e29795bdd52"}, ] [package.dependencies] @@ -4563,22 +4583,22 @@ deprecated = ">=1.2.6" googleapis-common-protos = ">=1.52,<2.0" grpcio = ">=1.0.0,<2.0.0" opentelemetry-api = ">=1.15,<2.0" -opentelemetry-exporter-otlp-proto-common = "1.19.0" -opentelemetry-proto = "1.19.0" -opentelemetry-sdk = ">=1.19.0,<1.20.0" +opentelemetry-exporter-otlp-proto-common = "1.20.0" +opentelemetry-proto = "1.20.0" +opentelemetry-sdk = ">=1.20.0,<1.21.0" [package.extras] test = ["pytest-grpc"] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_exporter_otlp_proto_http-1.19.0-py3-none-any.whl", hash = "sha256:4e050ca57819519a3cb8a6b17feac0d3b4b115796674b6a26295036ae941e11a"}, - {file = "opentelemetry_exporter_otlp_proto_http-1.19.0.tar.gz", hash = "sha256:7c8d2c1268cef4d9c1b13a1399f510e3c3dbb88c52f54597d487a128a23b681e"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.20.0-py3-none-any.whl", hash = "sha256:03f6e768ad25f1c3a9586e8c695db4a4adf978f8546a1285fa962e16bfbb0bd6"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.20.0.tar.gz", hash = "sha256:500f42821420fdf0759193d6438edc0f4e984a83e14c08a23023c06a188861b4"}, ] [package.dependencies] @@ -4586,9 +4606,9 @@ backoff = {version = ">=1.10.0,<3.0.0", markers = "python_version >= \"3.7\""} deprecated = ">=1.2.6" googleapis-common-protos = ">=1.52,<2.0" opentelemetry-api = ">=1.15,<2.0" -opentelemetry-exporter-otlp-proto-common = "1.19.0" -opentelemetry-proto = "1.19.0" -opentelemetry-sdk = ">=1.19.0,<1.20.0" +opentelemetry-exporter-otlp-proto-common = "1.20.0" +opentelemetry-proto = "1.20.0" +opentelemetry-sdk = ">=1.20.0,<1.21.0" requests = ">=2.7,<3.0" [package.extras] @@ -4612,13 +4632,13 @@ prometheus-client = ">=0.5.0,<1.0.0" [[package]] name = "opentelemetry-instrumentation" -version = "0.40b0" +version = "0.41b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_instrumentation-0.40b0-py3-none-any.whl", hash = "sha256:789d3726e698aa9526dd247b461b9172f99a4345571546c4aecf40279679fc8e"}, - {file = "opentelemetry_instrumentation-0.40b0.tar.gz", hash = "sha256:08bebe6a752514ed61e901e9fee5ccf06ae7533074442e707d75bb65f3e0aa17"}, + {file = "opentelemetry_instrumentation-0.41b0-py3-none-any.whl", hash = "sha256:0ef9e5705ceca0205992a4a845ae4251ce6ec15a1206ca07c2b00afb0c5bd386"}, + {file = "opentelemetry_instrumentation-0.41b0.tar.gz", hash = "sha256:214382ba10dfd29d4e24898a4c7ef18b7368178a6277a1aec95cdb75cabf4612"}, ] [package.dependencies] @@ -4628,20 +4648,20 @@ wrapt = ">=1.0.0,<2.0.0" [[package]] name = "opentelemetry-instrumentation-aiohttp-client" -version = "0.40b0" +version = "0.41b0" description = "OpenTelemetry aiohttp client instrumentation" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_instrumentation_aiohttp_client-0.40b0-py3-none-any.whl", hash = "sha256:195c7089636c7639463fbc588b79bc0cf41dd2971f7da1e99a9d4433249d058d"}, - {file = "opentelemetry_instrumentation_aiohttp_client-0.40b0.tar.gz", hash = "sha256:4de0a72b922d391183d00e2a1cc32e52b88dd2a6c77311b166e566b125b19943"}, + {file = "opentelemetry_instrumentation_aiohttp_client-0.41b0-py3-none-any.whl", hash = "sha256:a1d0d18dee5e57cf9187d1a561f9d4ce56d16433231208405458358ff6399a6f"}, + {file = "opentelemetry_instrumentation_aiohttp_client-0.41b0.tar.gz", hash = "sha256:56fd35e90c2534b2647e7cdd85f34383eddaa300ee51e989c3763dcdb205ca91"}, ] [package.dependencies] opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.40b0" -opentelemetry-semantic-conventions = "0.40b0" -opentelemetry-util-http = "0.40b0" +opentelemetry-instrumentation = "0.41b0" +opentelemetry-semantic-conventions = "0.41b0" +opentelemetry-util-http = "0.41b0" wrapt = ">=1.0.0,<2.0.0" [package.extras] @@ -4650,79 +4670,79 @@ test = ["http-server-mock", "opentelemetry-instrumentation-aiohttp-client[instru [[package]] name = "opentelemetry-instrumentation-asgi" -version = "0.40b0" +version = "0.41b0" description = "ASGI instrumentation for OpenTelemetry" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_instrumentation_asgi-0.40b0-py3-none-any.whl", hash = "sha256:3fc6940bacaccf2d96c24bd937a46dade28b930df8ec4e1231f612f2a66e4496"}, - {file = "opentelemetry_instrumentation_asgi-0.40b0.tar.gz", hash = "sha256:98678ef9e3856746dd52b11b8c6ce258acc70ecb910c9053ad7aaabab8fa71f2"}, + {file = "opentelemetry_instrumentation_asgi-0.41b0-py3-none-any.whl", hash = "sha256:46084195fb9c50507abbe1dd490ae4c31c8658c5790f1ddf7af95c417dbe6422"}, + {file = "opentelemetry_instrumentation_asgi-0.41b0.tar.gz", hash = "sha256:921244138b37a9a25edf2153f1c248f16f98610ee8d840b25fd7bf6b165e4d72"}, ] [package.dependencies] asgiref = ">=3.0,<4.0" opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.40b0" -opentelemetry-semantic-conventions = "0.40b0" -opentelemetry-util-http = "0.40b0" +opentelemetry-instrumentation = "0.41b0" +opentelemetry-semantic-conventions = "0.41b0" +opentelemetry-util-http = "0.41b0" [package.extras] instruments = ["asgiref (>=3.0,<4.0)"] -test = ["opentelemetry-instrumentation-asgi[instruments]", "opentelemetry-test-utils (==0.40b0)"] +test = ["opentelemetry-instrumentation-asgi[instruments]", "opentelemetry-test-utils (==0.41b0)"] [[package]] name = "opentelemetry-instrumentation-fastapi" -version = "0.40b0" +version = "0.41b0" description = "OpenTelemetry FastAPI Instrumentation" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_instrumentation_fastapi-0.40b0-py3-none-any.whl", hash = "sha256:2d71b41b460ebadc347a87ef6c9595864965745a7752b1e08d064eea327e350e"}, - {file = "opentelemetry_instrumentation_fastapi-0.40b0.tar.gz", hash = "sha256:d97276a4bda9155de74b0761cdf52831d22fb93894b644ebba026501aa6f7a94"}, + {file = "opentelemetry_instrumentation_fastapi-0.41b0-py3-none-any.whl", hash = "sha256:5990368e99ecc989df0a248a0b9b8e85d8b3eb7c1dbf5131c36982ba7f4a43b7"}, + {file = "opentelemetry_instrumentation_fastapi-0.41b0.tar.gz", hash = "sha256:eb4ceefe8b944fc9ea5e61fa558b99afd1285431b563f3f0104ac177cde4dfe5"}, ] [package.dependencies] opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.40b0" -opentelemetry-instrumentation-asgi = "0.40b0" -opentelemetry-semantic-conventions = "0.40b0" -opentelemetry-util-http = "0.40b0" +opentelemetry-instrumentation = "0.41b0" +opentelemetry-instrumentation-asgi = "0.41b0" +opentelemetry-semantic-conventions = "0.41b0" +opentelemetry-util-http = "0.41b0" [package.extras] instruments = ["fastapi (>=0.58,<1.0)"] -test = ["httpx (>=0.22,<1.0)", "opentelemetry-instrumentation-fastapi[instruments]", "opentelemetry-test-utils (==0.40b0)", "requests (>=2.23,<3.0)"] +test = ["httpx (>=0.22,<1.0)", "opentelemetry-instrumentation-fastapi[instruments]", "opentelemetry-test-utils (==0.41b0)", "requests (>=2.23,<3.0)"] [[package]] name = "opentelemetry-instrumentation-grpc" -version = "0.40b0" +version = "0.41b0" description = "OpenTelemetry gRPC instrumentation" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_instrumentation_grpc-0.40b0-py3-none-any.whl", hash = "sha256:36904f1c25a29465efcb1cc3f90b5871b462f547cd6ab244c5ea6185872eb507"}, - {file = "opentelemetry_instrumentation_grpc-0.40b0.tar.gz", hash = "sha256:c8ac3a1db5553f17aa0d071ac0a3f520da0ef1e9c6f1d1625b211fa58b084a7f"}, + {file = "opentelemetry_instrumentation_grpc-0.41b0-py3-none-any.whl", hash = "sha256:da08f5a545655079bf3333ede05491915e11f04b21e1a511c54f001e7fb9cb22"}, + {file = "opentelemetry_instrumentation_grpc-0.41b0.tar.gz", hash = "sha256:0a0df7bb974eab9a0b5750410114067602129f3b641b5d73d3f0fb3526be7847"}, ] [package.dependencies] opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.40b0" +opentelemetry-instrumentation = "0.41b0" opentelemetry-sdk = ">=1.12,<2.0" -opentelemetry-semantic-conventions = "0.40b0" +opentelemetry-semantic-conventions = "0.41b0" wrapt = ">=1.0.0,<2.0.0" [package.extras] instruments = ["grpcio (>=1.27,<2.0)"] -test = ["opentelemetry-instrumentation-grpc[instruments]", "opentelemetry-sdk (>=1.12,<2.0)", "opentelemetry-test-utils (==0.40b0)", "protobuf (>=3.13,<4.0)"] +test = ["opentelemetry-instrumentation-grpc[instruments]", "opentelemetry-sdk (>=1.12,<2.0)", "opentelemetry-test-utils (==0.41b0)", "protobuf (>=3.13,<4.0)"] [[package]] name = "opentelemetry-proto" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_proto-1.19.0-py3-none-any.whl", hash = "sha256:d06391cb5fa0fab111b42d3adf5f22683748745bd8f59ed4acfd93cfd252b960"}, - {file = "opentelemetry_proto-1.19.0.tar.gz", hash = "sha256:be53205622d85ecd37ebbf764aed907d87620a45eae638860cb5a778bf900c04"}, + {file = "opentelemetry_proto-1.20.0-py3-none-any.whl", hash = "sha256:512c3d2c6864fb7547a69577c3907348e6c985b7a204533563cb4c4c5046203b"}, + {file = "opentelemetry_proto-1.20.0.tar.gz", hash = "sha256:cf01f49b3072ee57468bccb1a4f93bdb55411f4512d0ac3f97c5c04c0040b5a2"}, ] [package.dependencies] @@ -4730,40 +4750,40 @@ protobuf = ">=3.19,<5.0" [[package]] name = "opentelemetry-sdk" -version = "1.19.0" +version = "1.20.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_sdk-1.19.0-py3-none-any.whl", hash = "sha256:bb67ad676b1bc671766a40d7fc9d9563854c186fa11f0dc8fa2284e004bd4263"}, - {file = "opentelemetry_sdk-1.19.0.tar.gz", hash = "sha256:765928956262c7a7766eaba27127b543fb40ef710499cad075f261f52163a87f"}, + {file = "opentelemetry_sdk-1.20.0-py3-none-any.whl", hash = "sha256:f2230c276ff4c63ea09b3cb2e2ac6b1265f90af64e8d16bbf275c81a9ce8e804"}, + {file = "opentelemetry_sdk-1.20.0.tar.gz", hash = "sha256:702e432a457fa717fd2ddfd30640180e69938f85bb7fec3e479f85f61c1843f8"}, ] [package.dependencies] -opentelemetry-api = "1.19.0" -opentelemetry-semantic-conventions = "0.40b0" +opentelemetry-api = "1.20.0" +opentelemetry-semantic-conventions = "0.41b0" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.40b0" +version = "0.41b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_semantic_conventions-0.40b0-py3-none-any.whl", hash = "sha256:7ebbaf86755a0948902e68637e3ae516c50222c30455e55af154ad3ffe283839"}, - {file = "opentelemetry_semantic_conventions-0.40b0.tar.gz", hash = "sha256:5a7a491873b15ab7c4907bbfd8737645cc87ca55a0a326c1755d1b928d8a0fae"}, + {file = "opentelemetry_semantic_conventions-0.41b0-py3-none-any.whl", hash = "sha256:45404391ed9e50998183a4925ad1b497c01c143f06500c3b9c3d0013492bb0f2"}, + {file = "opentelemetry_semantic_conventions-0.41b0.tar.gz", hash = "sha256:0ce5b040b8a3fc816ea5879a743b3d6fe5db61f6485e4def94c6ee4d402e1eb7"}, ] [[package]] name = "opentelemetry-util-http" -version = "0.40b0" +version = "0.41b0" description = "Web util for OpenTelemetry" optional = false python-versions = ">=3.7" files = [ - {file = "opentelemetry_util_http-0.40b0-py3-none-any.whl", hash = "sha256:7e071110b724a24d70de7441aeb4541bf8495ba5f5c33927e6b806d597379d0f"}, - {file = "opentelemetry_util_http-0.40b0.tar.gz", hash = "sha256:47d93efa1bb6c71954a5c6ae29a9546efae77f6875dc6c807a76898e0d478b80"}, + {file = "opentelemetry_util_http-0.41b0-py3-none-any.whl", hash = "sha256:6a167fd1e0e8b0f629530d971165b5d82ed0be2154b7f29498499c3a517edee5"}, + {file = "opentelemetry_util_http-0.41b0.tar.gz", hash = "sha256:16d5bd04a380dc1079e766562d1e1626cbb47720f197f67010c45f090fffdfb3"}, ] [[package]] @@ -4855,61 +4875,69 @@ files = [ [[package]] name = "pandas" -version = "2.1.0" +version = "2.0.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "pandas-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40dd20439ff94f1b2ed55b393ecee9cb6f3b08104c2c40b0cb7186a2f0046242"}, - {file = "pandas-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4f38e4fedeba580285eaac7ede4f686c6701a9e618d8a857b138a126d067f2f"}, - {file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6a0fe052cf27ceb29be9429428b4918f3740e37ff185658f40d8702f0b3e09"}, - {file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d81e1813191070440d4c7a413cb673052b3b4a984ffd86b8dd468c45742d3cc"}, - {file = "pandas-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb20252720b1cc1b7d0b2879ffc7e0542dd568f24d7c4b2347cb035206936421"}, - {file = "pandas-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:38f74ef7ebc0ffb43b3d633e23d74882bce7e27bfa09607f3c5d3e03ffd9a4a5"}, - {file = "pandas-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cda72cc8c4761c8f1d97b169661f23a86b16fdb240bdc341173aee17e4d6cedd"}, - {file = "pandas-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d97daeac0db8c993420b10da4f5f5b39b01fc9ca689a17844e07c0a35ac96b4b"}, - {file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c58b1113892e0c8078f006a167cc210a92bdae23322bb4614f2f0b7a4b510f"}, - {file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:629124923bcf798965b054a540f9ccdfd60f71361255c81fa1ecd94a904b9dd3"}, - {file = "pandas-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:70cf866af3ab346a10debba8ea78077cf3a8cd14bd5e4bed3d41555a3280041c"}, - {file = "pandas-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d53c8c1001f6a192ff1de1efe03b31a423d0eee2e9e855e69d004308e046e694"}, - {file = "pandas-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f100b3876b8c6d1a2c66207288ead435dc71041ee4aea789e55ef0e06408cb"}, - {file = "pandas-2.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28f330845ad21c11db51e02d8d69acc9035edfd1116926ff7245c7215db57957"}, - {file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a6ccf0963db88f9b12df6720e55f337447aea217f426a22d71f4213a3099a6"}, - {file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99e678180bc59b0c9443314297bddce4ad35727a1a2656dbe585fd78710b3b9"}, - {file = "pandas-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b31da36d376d50a1a492efb18097b9101bdbd8b3fbb3f49006e02d4495d4c644"}, - {file = "pandas-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0164b85937707ec7f70b34a6c3a578dbf0f50787f910f21ca3b26a7fd3363437"}, - {file = "pandas-2.1.0.tar.gz", hash = "sha256:62c24c7fc59e42b775ce0679cfa7b14a5f9bfb7643cfbe708c960699e05fb918"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, ] [package.dependencies] -numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""} +numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, +] python-dateutil = ">=2.8.2" pytz = ">=2020.1" tzdata = ">=2022.1" [package.extras] -all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"] -aws = ["s3fs (>=2022.05.0)"] -clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"] -compression = ["zstandard (>=0.17.0)"] -computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] feather = ["pyarrow (>=7.0.0)"] -fss = ["fsspec (>=2022.05.0)"] -gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"] -hdf5 = ["tables (>=3.7.0)"] -html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"] -mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] parquet = ["pyarrow (>=7.0.0)"] -performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] plot = ["matplotlib (>=3.6.1)"] -postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"] -spss = ["pyreadstat (>=1.1.5)"] -sql-other = ["SQLAlchemy (>=1.4.36)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.8.0)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] [[package]] name = "pandas-stubs" @@ -5030,65 +5058,65 @@ files = [ [[package]] name = "pillow" -version = "10.0.0" +version = "10.0.1" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "Pillow-10.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891"}, - {file = "Pillow-10.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614"}, - {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b"}, - {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c"}, - {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1"}, - {file = "Pillow-10.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf"}, - {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3"}, - {file = "Pillow-10.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992"}, - {file = "Pillow-10.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de"}, - {file = "Pillow-10.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485"}, - {file = "Pillow-10.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f"}, - {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3"}, - {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d"}, - {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd"}, - {file = "Pillow-10.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629"}, - {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, - {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, - {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, - {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, - {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, - {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, - {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568"}, - {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223"}, - {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff"}, - {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, - {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, - {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, - {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, - {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, - {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, - {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d"}, - {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530"}, - {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51"}, - {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86"}, - {file = "Pillow-10.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7"}, - {file = "Pillow-10.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0"}, - {file = "Pillow-10.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa"}, - {file = "Pillow-10.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90"}, - {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967"}, - {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d"}, - {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3"}, - {file = "Pillow-10.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba"}, - {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3"}, - {file = "Pillow-10.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017"}, - {file = "Pillow-10.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6"}, - {file = "Pillow-10.0.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0"}, - {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba"}, - {file = "Pillow-10.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3"}, - {file = "Pillow-10.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334"}, - {file = "Pillow-10.0.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2"}, - {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed"}, - {file = "Pillow-10.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684"}, - {file = "Pillow-10.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3"}, - {file = "Pillow-10.0.0.tar.gz", hash = "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396"}, + {file = "Pillow-10.0.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:8f06be50669087250f319b706decf69ca71fdecd829091a37cc89398ca4dc17a"}, + {file = "Pillow-10.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50bd5f1ebafe9362ad622072a1d2f5850ecfa44303531ff14353a4059113b12d"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6a90167bcca1216606223a05e2cf991bb25b14695c518bc65639463d7db722d"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11c9102c56ffb9ca87134bd025a43d2aba3f1155f508eff88f694b33a9c6d19"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:186f7e04248103482ea6354af6d5bcedb62941ee08f7f788a1c7707bc720c66f"}, + {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0462b1496505a3462d0f35dc1c4d7b54069747d65d00ef48e736acda2c8cbdff"}, + {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d889b53ae2f030f756e61a7bff13684dcd77e9af8b10c6048fb2c559d6ed6eaf"}, + {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:552912dbca585b74d75279a7570dd29fa43b6d93594abb494ebb31ac19ace6bd"}, + {file = "Pillow-10.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:787bb0169d2385a798888e1122c980c6eff26bf941a8ea79747d35d8f9210ca0"}, + {file = "Pillow-10.0.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"}, + {file = "Pillow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205"}, + {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2"}, + {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b"}, + {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1"}, + {file = "Pillow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088"}, + {file = "Pillow-10.0.1-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b"}, + {file = "Pillow-10.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a"}, + {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91"}, + {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4"}, + {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08"}, + {file = "Pillow-10.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08"}, + {file = "Pillow-10.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:764d2c0daf9c4d40ad12fbc0abd5da3af7f8aa11daf87e4fa1b834000f4b6b0a"}, + {file = "Pillow-10.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcb59711009b0168d6ee0bd8fb5eb259c4ab1717b2f538bbf36bacf207ef7a68"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:697a06bdcedd473b35e50a7e7506b1d8ceb832dc238a336bd6f4f5aa91a4b500"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f665d1e6474af9f9da5e86c2a3a2d2d6204e04d5af9c06b9d42afa6ebde3f21"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:2fa6dd2661838c66f1a5473f3b49ab610c98a128fc08afbe81b91a1f0bf8c51d"}, + {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:3a04359f308ebee571a3127fdb1bd01f88ba6f6fb6d087f8dd2e0d9bff43f2a7"}, + {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:723bd25051454cea9990203405fa6b74e043ea76d4968166dfd2569b0210886a"}, + {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:71671503e3015da1b50bd18951e2f9daf5b6ffe36d16f1eb2c45711a301521a7"}, + {file = "Pillow-10.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:44e7e4587392953e5e251190a964675f61e4dae88d1e6edbe9f36d6243547ff3"}, + {file = "Pillow-10.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:3855447d98cced8670aaa63683808df905e956f00348732448b5a6df67ee5849"}, + {file = "Pillow-10.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed2d9c0704f2dc4fa980b99d565c0c9a543fe5101c25b3d60488b8ba80f0cce1"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5bb289bb835f9fe1a1e9300d011eef4d69661bb9b34d5e196e5e82c4cb09b37"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0d3e54ab1df9df51b914b2233cf779a5a10dfd1ce339d0421748232cea9876"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2cc6b86ece42a11f16f55fe8903595eff2b25e0358dec635d0a701ac9586588f"}, + {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ca26ba5767888c84bf5a0c1a32f069e8204ce8c21d00a49c90dabeba00ce0145"}, + {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f0b4b06da13275bc02adfeb82643c4a6385bd08d26f03068c2796f60d125f6f2"}, + {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bc2e3069569ea9dbe88d6b8ea38f439a6aad8f6e7a6283a38edf61ddefb3a9bf"}, + {file = "Pillow-10.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8b451d6ead6e3500b6ce5c7916a43d8d8d25ad74b9102a629baccc0808c54971"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4"}, + {file = "Pillow-10.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d"}, + {file = "Pillow-10.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d"}, + {file = "Pillow-10.0.1.tar.gz", hash = "sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d"}, ] [package.extras] @@ -5097,13 +5125,13 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa [[package]] name = "pinecone-client" -version = "2.2.2" +version = "2.2.4" description = "Pinecone client and SDK" optional = false python-versions = ">=3.8" files = [ - {file = "pinecone-client-2.2.2.tar.gz", hash = "sha256:391fe413754efd4e0ef00154b44271d63c4cdd4bedf088d23111a5725d863210"}, - {file = "pinecone_client-2.2.2-py3-none-any.whl", hash = "sha256:21fddb752668efee4d3c6b706346d9580e36a8b06b8d97afd60bd33ef2536e7e"}, + {file = "pinecone-client-2.2.4.tar.gz", hash = "sha256:2c1cc1d6648b2be66e944db2ffa59166a37b9164d1135ad525d9cd8b1e298168"}, + {file = "pinecone_client-2.2.4-py3-none-any.whl", hash = "sha256:5bf496c01c2f82f4e5c2dc977cc5062ecd7168b8ed90743b09afcc8c7eb242ec"}, ] [package.dependencies] @@ -5118,7 +5146,7 @@ typing-extensions = ">=3.7.4" urllib3 = ">=1.21.1" [package.extras] -grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv2 (==0.1.0)", "grpcio (>=1.44.0)", "lz4 (>=3.1.3)", "protobuf (>=3.19.5,<3.20.0)"] +grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv2 (==0.1.0)", "grpcio (>=1.44.0)", "lz4 (>=3.1.3)", "protobuf (>=3.20.0,<3.21.0)"] [[package]] name = "pkginfo" @@ -5166,13 +5194,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "portalocker" -version = "2.7.0" +version = "2.8.2" description = "Wraps the portalocker recipe for easy usage" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "portalocker-2.7.0-py2.py3-none-any.whl", hash = "sha256:a07c5b4f3985c3cf4798369631fb7011adb498e2a46d8440efc75a8f29a0f983"}, - {file = "portalocker-2.7.0.tar.gz", hash = "sha256:032e81d534a88ec1736d03f780ba073f047a06c478b06e2937486f334e955c51"}, + {file = "portalocker-2.8.2-py3-none-any.whl", hash = "sha256:cfb86acc09b9aa7c3b43594e19be1345b9d16af3feb08bf92f23d4dce513a28e"}, + {file = "portalocker-2.8.2.tar.gz", hash = "sha256:2b035aa7828e46c58e9b31390ee1f169b98e1066ab10b9a6a861fe7e25ee4f33"}, ] [package.dependencies] @@ -5181,7 +5209,7 @@ pywin32 = {version = ">=226", markers = "platform_system == \"Windows\""} [package.extras] docs = ["sphinx (>=1.7.1)"] redis = ["redis"] -tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)"] +tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)", "types-redis"] [[package]] name = "postgrest" @@ -5830,13 +5858,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pypdf" -version = "3.15.5" +version = "3.16.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.6" files = [ - {file = "pypdf-3.15.5-py3-none-any.whl", hash = "sha256:8e003c4ee4875450612c2571ba9a5cc12d63a46b226a484314b21b7f013d2717"}, - {file = "pypdf-3.15.5.tar.gz", hash = "sha256:81cf6e8a206450726555023a36c13fb40f680c047b8fcc0bcbfd4d1908c33d31"}, + {file = "pypdf-3.16.0-py3-none-any.whl", hash = "sha256:887bf97029d9317a1a48da71c3fae7d6fab905a8b2e82999004e9d1385616a6a"}, + {file = "pypdf-3.16.0.tar.gz", hash = "sha256:71fd274f5e02c7122f688f5b2609407d5dd92ecb4140d498108fc94ea9573800"}, ] [package.dependencies] @@ -5875,13 +5903,13 @@ chardet = "*" [[package]] name = "pytest" -version = "7.4.1" +version = "7.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.1-py3-none-any.whl", hash = "sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f"}, - {file = "pytest-7.4.1.tar.gz", hash = "sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab"}, + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] @@ -6303,13 +6331,13 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qdrant-client" -version = "1.4.0" +version = "1.5.4" description = "Client library for the Qdrant vector search engine" optional = false -python-versions = ">=3.7,<3.12" +python-versions = ">=3.8,<3.12" files = [ - {file = "qdrant_client-1.4.0-py3-none-any.whl", hash = "sha256:2f9e563955b5163da98016f2ed38d9aea5058576c7c5844e9aa205d28155f56d"}, - {file = "qdrant_client-1.4.0.tar.gz", hash = "sha256:2e54f5a80eb1e7e67f4603b76365af4817af15fb3d0c0f44de4fd93afbbe5537"}, + {file = "qdrant_client-1.5.4-py3-none-any.whl", hash = "sha256:b0247886c51d755f70dc1a62545f38ba5c7d72971ab533f1264fb695c21a6d8f"}, + {file = "qdrant_client-1.5.4.tar.gz", hash = "sha256:6ffbca94f7cab23230001710b7dc04684dbc18dadf66982179a37531b4c4b178"}, ] [package.dependencies] @@ -6321,20 +6349,23 @@ portalocker = ">=2.7.0,<3.0.0" pydantic = ">=1.10.8" urllib3 = ">=1.26.14,<2.0.0" +[package.extras] +fastembed = ["fastembed (==0.0.4)"] + [[package]] name = "readme-renderer" -version = "41.0" -description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" +version = "42.0" +description = "readme_renderer is a library for rendering readme descriptions for Warehouse" optional = false python-versions = ">=3.8" files = [ - {file = "readme_renderer-41.0-py3-none-any.whl", hash = "sha256:a38243d5b6741b700a850026e62da4bd739edc7422071e95fd5c4bb60171df86"}, - {file = "readme_renderer-41.0.tar.gz", hash = "sha256:4f4b11e5893f5a5d725f592c5a343e0dc74f5f273cb3dcf8c42d9703a27073f7"}, + {file = "readme_renderer-42.0-py3-none-any.whl", hash = "sha256:13d039515c1f24de668e2c93f2e877b9dbe6c6c32328b90a40a49d8b2b85f36d"}, + {file = "readme_renderer-42.0.tar.gz", hash = "sha256:2d55489f83be4992fe4454939d1a051c33edbab778e82761d060c9fc6b308cd1"}, ] [package.dependencies] -bleach = ">=2.1.0" docutils = ">=0.13.1" +nh3 = ">=0.2.14" Pygments = ">=2.5.1" [package.extras] @@ -6592,63 +6623,21 @@ files = [ ] [[package]] -name = "safetensors" -version = "0.3.3" -description = "Fast and Safe Tensor serialization" +name = "sacremoses" +version = "0.0.53" +description = "SacreMoses" optional = true python-versions = "*" files = [ - {file = "safetensors-0.3.3-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:92e4d0c8b2836120fddd134474c5bda8963f322333941f8b9f643e5b24f041eb"}, - {file = "safetensors-0.3.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3dcadb6153c42addc9c625a622ebde9293fabe1973f9ef31ba10fb42c16e8536"}, - {file = "safetensors-0.3.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:08f26b61e1b0a14dc959aa9d568776bd038805f611caef1de04a80c468d4a7a4"}, - {file = "safetensors-0.3.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:17f41344d9a075f2f21b289a49a62e98baff54b5754240ba896063bce31626bf"}, - {file = "safetensors-0.3.3-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:f1045f798e1a16a6ced98d6a42ec72936d367a2eec81dc5fade6ed54638cd7d2"}, - {file = "safetensors-0.3.3-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:eaf0e4bc91da13f21ac846a39429eb3f3b7ed06295a32321fa3eb1a59b5c70f3"}, - {file = "safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a07121f427e646a50d18c1be0fa1a2cbf6398624c31149cd7e6b35486d72189e"}, - {file = "safetensors-0.3.3-cp310-cp310-win32.whl", hash = "sha256:a85e29cbfddfea86453cc0f4889b4bcc6b9c155be9a60e27be479a34e199e7ef"}, - {file = "safetensors-0.3.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cbc3312f134baf07334dd517341a4b470b2931f090bd9284888acb7dfaf4606f"}, - {file = "safetensors-0.3.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d15030af39d5d30c22bcbc6d180c65405b7ea4c05b7bab14a570eac7d7d43722"}, - {file = "safetensors-0.3.3-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:f84a74cbe9859b28e3d6d7715ac1dd3097bebf8d772694098f6d42435245860c"}, - {file = "safetensors-0.3.3-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:10d637423d98ab2e6a4ad96abf4534eb26fcaf8ca3115623e64c00759374e90d"}, - {file = "safetensors-0.3.3-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:3b46f5de8b44084aff2e480874c550c399c730c84b2e8ad1bddb062c94aa14e9"}, - {file = "safetensors-0.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e8fdf7407dba44587ed5e79d5de3533d242648e1f2041760b21474bd5ea5c8c"}, - {file = "safetensors-0.3.3-cp311-cp311-win32.whl", hash = "sha256:7d3b744cee8d7a46ffa68db1a2ff1a1a432488e3f7a5a97856fe69e22139d50c"}, - {file = "safetensors-0.3.3-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:2fff5b19a1b462c17322998b2f4b8bce43c16fe208968174d2f3a1446284ceed"}, - {file = "safetensors-0.3.3-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:41adb1d39e8aad04b16879e3e0cbcb849315999fad73bc992091a01e379cb058"}, - {file = "safetensors-0.3.3-cp37-cp37m-macosx_12_0_x86_64.whl", hash = "sha256:0f2b404250b3b877b11d34afcc30d80e7035714a1116a3df56acaca6b6c00096"}, - {file = "safetensors-0.3.3-cp37-cp37m-macosx_13_0_x86_64.whl", hash = "sha256:b43956ef20e9f4f2e648818a9e7b3499edd6b753a0f5526d4f6a6826fbee8446"}, - {file = "safetensors-0.3.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c32ee08f61cea56a5d62bbf94af95df6040c8ab574afffaeb7b44ae5da1e9e3"}, - {file = "safetensors-0.3.3-cp37-cp37m-win32.whl", hash = "sha256:351600f367badd59f7bfe86d317bb768dd8c59c1561c6fac43cafbd9c1af7827"}, - {file = "safetensors-0.3.3-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:8530399666748634bc0b301a6a5523756931b0c2680d188e743d16304afe917a"}, - {file = "safetensors-0.3.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:9d741c1f1621e489ba10aa3d135b54202684f6e205df52e219d5eecd673a80c9"}, - {file = "safetensors-0.3.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:0c345fd85b4d2093a5109596ff4cd9dfc2e84992e881b4857fbc4a93a3b89ddb"}, - {file = "safetensors-0.3.3-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:69ccee8d05f55cdf76f7e6c87d2bdfb648c16778ef8acfd2ecc495e273e9233e"}, - {file = "safetensors-0.3.3-cp38-cp38-macosx_13_0_arm64.whl", hash = "sha256:c08a9a4b7a4ca389232fa8d097aebc20bbd4f61e477abc7065b5c18b8202dede"}, - {file = "safetensors-0.3.3-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:a002868d2e3f49bbe81bee2655a411c24fa1f8e68b703dec6629cb989d6ae42e"}, - {file = "safetensors-0.3.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab43aeeb9eadbb6b460df3568a662e6f1911ecc39387f8752afcb6a7d96c087"}, - {file = "safetensors-0.3.3-cp38-cp38-win32.whl", hash = "sha256:f2f59fce31dd3429daca7269a6b06f65e6547a0c248f5116976c3f1e9b73f251"}, - {file = "safetensors-0.3.3-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:59a596b3225c96d59af412385981f17dd95314e3fffdf359c7e3f5bb97730a19"}, - {file = "safetensors-0.3.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:82a16e92210a6221edd75ab17acdd468dd958ef5023d9c6c1289606cc30d1479"}, - {file = "safetensors-0.3.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:98a929e763a581f516373ef31983ed1257d2d0da912a8e05d5cd12e9e441c93a"}, - {file = "safetensors-0.3.3-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:12b83f1986cd16ea0454c636c37b11e819d60dd952c26978310a0835133480b7"}, - {file = "safetensors-0.3.3-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:f439175c827c2f1bbd54df42789c5204a10983a30bc4242bc7deaf854a24f3f0"}, - {file = "safetensors-0.3.3-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:0085be33b8cbcb13079b3a8e131656e05b0bc5e6970530d4c24150f7afd76d70"}, - {file = "safetensors-0.3.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad3cc8006e7a86ee7c88bd2813ec59cd7cc75b03e6fa4af89b9c7b235b438d68"}, - {file = "safetensors-0.3.3-cp39-cp39-win32.whl", hash = "sha256:ab29f54c6b8c301ca05fa014728996bd83aac6e21528f893aaf8945c71f42b6d"}, - {file = "safetensors-0.3.3.tar.gz", hash = "sha256:edb7072d788c4f929d0f5735d3a2fb51e5a27f833587828583b7f5747af1a2b8"}, + {file = "sacremoses-0.0.53.tar.gz", hash = "sha256:43715868766c643b35de4b8046cce236bfe59a7fa88b25eaf6ddf02bacf53a7a"}, ] -[package.extras] -all = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (==2.11.0)", "torch (>=1.10)"] -dev = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (==2.11.0)", "torch (>=1.10)"] -jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)"] -numpy = ["numpy (>=1.21.6)"] -paddlepaddle = ["numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)"] -pinned-tf = ["tensorflow (==2.11.0)"] -quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] -tensorflow = ["numpy (>=1.21.6)", "tensorflow (>=2.11.0)"] -testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "numpy (>=1.21.6)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)"] -torch = ["numpy (>=1.21.6)", "torch (>=1.10)"] +[package.dependencies] +click = "*" +joblib = "*" +regex = "*" +six = "*" +tqdm = "*" [[package]] name = "scikit-learn" @@ -6838,19 +6827,19 @@ files = [ [[package]] name = "setuptools" -version = "68.1.2" +version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, - {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, + {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, + {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shapely" @@ -6942,18 +6931,18 @@ testing-without-asyncio = ["Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "Werk [[package]] name = "slack-sdk" -version = "3.21.3" +version = "3.22.0" description = "The Slack API Platform SDK for Python" optional = true python-versions = ">=3.6.0" files = [ - {file = "slack_sdk-3.21.3-py2.py3-none-any.whl", hash = "sha256:de3c07b92479940b61cd68c566f49fbc9974c8f38f661d26244078f3903bb9cc"}, - {file = "slack_sdk-3.21.3.tar.gz", hash = "sha256:20829bdc1a423ec93dac903470975ebf3bc76fd3fd91a4dadc0eeffc940ecb0c"}, + {file = "slack_sdk-3.22.0-py2.py3-none-any.whl", hash = "sha256:f102a4902115dff3b97c3e8883ad4e22d54732221886fc5ef29bfc290f063b4a"}, + {file = "slack_sdk-3.22.0.tar.gz", hash = "sha256:6eacce0fa4f8cfb4d84eac0d7d7e1b1926040a2df654ae86b94179bdf2bc4d8c"}, ] [package.extras] optional = ["SQLAlchemy (>=1.4,<3)", "aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "websocket-client (>=1,<2)", "websockets (>=10,<11)"] -testing = ["Flask (>=1,<2)", "Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "Werkzeug (<2)", "black (==22.8.0)", "boto3 (<=2)", "click (==8.0.4)", "databases (>=0.5)", "flake8 (>=5,<6)", "itsdangerous (==1.1.0)", "moto (>=3,<4)", "psutil (>=5,<6)", "pytest (>=6.2.5,<7)", "pytest-asyncio (<1)", "pytest-cov (>=2,<3)"] +testing = ["Flask (>=1,<2)", "Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "Werkzeug (<2)", "black (==22.8.0)", "boto3 (<=2)", "click (==8.0.4)", "flake8 (>=5,<6)", "itsdangerous (==1.1.0)", "moto (>=3,<4)", "psutil (>=5,<6)", "pytest (>=6.2.5,<7)", "pytest-asyncio (<1)", "pytest-cov (>=2,<3)"] [[package]] name = "smmap" @@ -7239,13 +7228,13 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"] [[package]] name = "textual" -version = "0.35.1" +version = "0.37.1" description = "Modern Text User Interface framework" optional = true python-versions = ">=3.7,<4.0" files = [ - {file = "textual-0.35.1-py3-none-any.whl", hash = "sha256:c4257ed3019cf8a2da2ac59ae59de5e66e04b95d482d065cfb3099f70fddd36f"}, - {file = "textual-0.35.1.tar.gz", hash = "sha256:70ca0bfe582f96dfa10179a9ab71329b8b15e750e26b7cee1fb4a67a981bbf36"}, + {file = "textual-0.37.1-py3-none-any.whl", hash = "sha256:bbedebd9bf245523dc07d1a883ce4178133cfe1d3c3e030a2224359128f177b7"}, + {file = "textual-0.37.1.tar.gz", hash = "sha256:0498894da7f4af5cac62d99e412e9d813e784f7a87834dd29aa656d31d068760"}, ] [package.dependencies] @@ -7312,56 +7301,117 @@ blobfile = ["blobfile (>=2)"] [[package]] name = "tokenizers" -version = "0.13.3" -description = "Fast and Customizable Tokenizers" +version = "0.14.0" +description = "" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "tokenizers-0.13.3-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:f3835c5be51de8c0a092058a4d4380cb9244fb34681fd0a295fbf0a52a5fdf33"}, - {file = "tokenizers-0.13.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4ef4c3e821730f2692489e926b184321e887f34fb8a6b80b8096b966ba663d07"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5fd1a6a25353e9aa762e2aae5a1e63883cad9f4e997c447ec39d071020459bc"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee0b1b311d65beab83d7a41c56a1e46ab732a9eed4460648e8eb0bd69fc2d059"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ef4215284df1277dadbcc5e17d4882bda19f770d02348e73523f7e7d8b8d396"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4d53976079cff8a033f778fb9adca2d9d69d009c02fa2d71a878b5f3963ed30"}, - {file = "tokenizers-0.13.3-cp310-cp310-win32.whl", hash = "sha256:1f0e3b4c2ea2cd13238ce43548959c118069db7579e5d40ec270ad77da5833ce"}, - {file = "tokenizers-0.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:89649c00d0d7211e8186f7a75dfa1db6996f65edce4b84821817eadcc2d3c79e"}, - {file = "tokenizers-0.13.3-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:56b726e0d2bbc9243872b0144515ba684af5b8d8cd112fb83ee1365e26ec74c8"}, - {file = "tokenizers-0.13.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc5c022ce692e1f499d745af293ab9ee6f5d92538ed2faf73f9708c89ee59ce6"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55c981ac44ba87c93e847c333e58c12abcbb377a0c2f2ef96e1a266e4184ff2"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f247eae99800ef821a91f47c5280e9e9afaeed9980fc444208d5aa6ba69ff148"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b3e3215d048e94f40f1c95802e45dcc37c5b05eb46280fc2ccc8cd351bff839"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ba2b0bf01777c9b9bc94b53764d6684554ce98551fec496f71bc5be3a03e98b"}, - {file = "tokenizers-0.13.3-cp311-cp311-win32.whl", hash = "sha256:cc78d77f597d1c458bf0ea7c2a64b6aa06941c7a99cb135b5969b0278824d808"}, - {file = "tokenizers-0.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:ecf182bf59bd541a8876deccf0360f5ae60496fd50b58510048020751cf1724c"}, - {file = "tokenizers-0.13.3-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:0527dc5436a1f6bf2c0327da3145687d3bcfbeab91fed8458920093de3901b44"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cbb2c307627dc99b44b22ef05ff4473aa7c7cc1fec8f0a8b37d8a64b1a16d2"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4560dbdeaae5b7ee0d4e493027e3de6d53c991b5002d7ff95083c99e11dd5ac0"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64064bd0322405c9374305ab9b4c07152a1474370327499911937fd4a76d004b"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8c6e2ab0f2e3d939ca66aa1d596602105fe33b505cd2854a4c1717f704c51de"}, - {file = "tokenizers-0.13.3-cp37-cp37m-win32.whl", hash = "sha256:6cc29d410768f960db8677221e497226e545eaaea01aa3613fa0fdf2cc96cff4"}, - {file = "tokenizers-0.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fc2a7fdf864554a0dacf09d32e17c0caa9afe72baf9dd7ddedc61973bae352d8"}, - {file = "tokenizers-0.13.3-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:8791dedba834c1fc55e5f1521be325ea3dafb381964be20684b92fdac95d79b7"}, - {file = "tokenizers-0.13.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:d607a6a13718aeb20507bdf2b96162ead5145bbbfa26788d6b833f98b31b26e1"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3791338f809cd1bf8e4fee6b540b36822434d0c6c6bc47162448deee3f77d425"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2f35f30e39e6aab8716f07790f646bdc6e4a853816cc49a95ef2a9016bf9ce6"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310204dfed5aa797128b65d63538a9837cbdd15da2a29a77d67eefa489edda26"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0f9b92ea052305166559f38498b3b0cae159caea712646648aaa272f7160963"}, - {file = "tokenizers-0.13.3-cp38-cp38-win32.whl", hash = "sha256:9a3fa134896c3c1f0da6e762d15141fbff30d094067c8f1157b9fdca593b5806"}, - {file = "tokenizers-0.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:8e7b0cdeace87fa9e760e6a605e0ae8fc14b7d72e9fc19c578116f7287bb873d"}, - {file = "tokenizers-0.13.3-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:00cee1e0859d55507e693a48fa4aef07060c4bb6bd93d80120e18fea9371c66d"}, - {file = "tokenizers-0.13.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a23ff602d0797cea1d0506ce69b27523b07e70f6dda982ab8cf82402de839088"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ce07445050b537d2696022dafb115307abdffd2a5c106f029490f84501ef97"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:280ffe95f50eaaf655b3a1dc7ff1d9cf4777029dbbc3e63a74e65a056594abc3"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97acfcec592f7e9de8cadcdcda50a7134423ac8455c0166b28c9ff04d227b371"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd7730c98a3010cd4f523465867ff95cd9d6430db46676ce79358f65ae39797b"}, - {file = "tokenizers-0.13.3-cp39-cp39-win32.whl", hash = "sha256:48625a108029cb1ddf42e17a81b5a3230ba6888a70c9dc14e81bc319e812652d"}, - {file = "tokenizers-0.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:bc0a6f1ba036e482db6453571c9e3e60ecd5489980ffd95d11dc9f960483d783"}, - {file = "tokenizers-0.13.3.tar.gz", hash = "sha256:2e546dbb68b623008a5442353137fbb0123d311a6d7ba52f2667c8862a75af2e"}, + {file = "tokenizers-0.14.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1a90e1030d9c61de64045206c62721a36f892dcfc5bbbc119dfcd417c1ca60ca"}, + {file = "tokenizers-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7cacc5a33767bb2a03b6090eac556c301a1d961ac2949be13977bc3f20cc4e3c"}, + {file = "tokenizers-0.14.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81994795e1b4f868a6e73107af8cdf088d31357bae6f7abf26c42874eab16f43"}, + {file = "tokenizers-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ec53f832bfa91abafecbf92b4259b466fb31438ab31e8291ade0fcf07de8fc2"}, + {file = "tokenizers-0.14.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:854aa813a55d6031a6399b1bca09e4e7a79a80ec05faeea77fc6809d59deb3d5"}, + {file = "tokenizers-0.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c34d2f02e25e0fa96e574cadb43a6f14bdefc77f84950991da6e3732489e164"}, + {file = "tokenizers-0.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f17d5ad725c827d3dc7db2bbe58093a33db2de49bbb639556a6d88d82f0ca19"}, + {file = "tokenizers-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:337a7b7d6b32c6f904faee4304987cb018d1488c88b91aa635760999f5631013"}, + {file = "tokenizers-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:98a7ceb767e1079ef2c99f52a4e7b816f2e682b2b6fef02c8eff5000536e54e1"}, + {file = "tokenizers-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25ad4a0f883a311a5b021ed979e21559cb4184242c7446cd36e07d046d1ed4be"}, + {file = "tokenizers-0.14.0-cp310-none-win32.whl", hash = "sha256:360706b0c2c6ba10e5e26b7eeb7aef106dbfc0a81ad5ad599a892449b4973b10"}, + {file = "tokenizers-0.14.0-cp310-none-win_amd64.whl", hash = "sha256:1c2ce437982717a5e221efa3c546e636f12f325cc3d9d407c91d2905c56593d0"}, + {file = "tokenizers-0.14.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:612d0ba4f40f4d41163af9613dac59c902d017dc4166ea4537a476af807d41c3"}, + {file = "tokenizers-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3013ad0cff561d9be9ce2cc92b76aa746b4e974f20e5b4158c03860a4c8ffe0f"}, + {file = "tokenizers-0.14.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c89a0d6d2ec393a6261df71063b1e22bdd7c6ef3d77b8826541b596132bcf524"}, + {file = "tokenizers-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5514417f37fc2ca8159b27853cd992a9a4982e6c51f04bd3ac3f65f68a8fa781"}, + {file = "tokenizers-0.14.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8e761fd1af8409c607b11f084dc7cc50f80f08bd426d4f01d1c353b097d2640f"}, + {file = "tokenizers-0.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c16fbcd5ef10df9e51cc84238cdb05ee37e4228aaff39c01aa12b0a0409e29b8"}, + {file = "tokenizers-0.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3439d9f858dd9033b69769be5a56eb4fb79fde13fad14fab01edbf2b98033ad9"}, + {file = "tokenizers-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c19f8cdc3e84090464a6e28757f60461388cc8cd41c02c109e180a6b7c571f6"}, + {file = "tokenizers-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:df763ce657a297eb73008d5907243a7558a45ae0930b38ebcb575a24f8296520"}, + {file = "tokenizers-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:095b0b6683a9b76002aa94659f75c09e4359cb291b318d6e77a60965d7a7f138"}, + {file = "tokenizers-0.14.0-cp311-none-win32.whl", hash = "sha256:712ec0e68a399ded8e115e7e25e7017802fa25ee6c36b4eaad88481e50d0c638"}, + {file = "tokenizers-0.14.0-cp311-none-win_amd64.whl", hash = "sha256:917aa6d6615b33d9aa811dcdfb3109e28ff242fbe2cb89ea0b7d3613e444a672"}, + {file = "tokenizers-0.14.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:8464ee7d43ecd9dd1723f51652f49b979052ea3bcd25329e3df44e950c8444d1"}, + {file = "tokenizers-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:84c2b96469b34825557c6fe0bc3154c98d15be58c416a9036ca90afdc9979229"}, + {file = "tokenizers-0.14.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:24b3ccec65ee6f876cd67251c1dcfa1c318c9beec5a438b134f7e33b667a8b36"}, + {file = "tokenizers-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde333fc56dd5fbbdf2de3067d6c0c129867d33eac81d0ba9b65752ad6ef4208"}, + {file = "tokenizers-0.14.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ddcc2f251bd8a2b2f9a7763ad4468a34cfc4ee3b0fba3cfb34d12c964950cac"}, + {file = "tokenizers-0.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10a34eb1416dcec3c6f9afea459acd18fcc93234687de605a768a987eda589ab"}, + {file = "tokenizers-0.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56bc7252530a6a20c6eed19b029914bb9cc781efbe943ca9530856051de99d0f"}, + {file = "tokenizers-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07f5c2324326a00c85111081d5eae4da9d64d56abb5883389b3c98bee0b50a7c"}, + {file = "tokenizers-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5efd92e44e43f36332b5f3653743dca5a0b72cdabb012f20023e220f01f675cb"}, + {file = "tokenizers-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9223bcb77a826dbc9fd0efa6bce679a96b1a01005142778bb42ce967581c5951"}, + {file = "tokenizers-0.14.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:e2c1b4707344d3fbfce35d76802c2429ca54e30a5ecb05b3502c1e546039a3bb"}, + {file = "tokenizers-0.14.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:5892ba10fe0a477bde80b9f06bce05cb9d83c15a4676dcae5cbe6510f4524bfc"}, + {file = "tokenizers-0.14.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0e1818f33ac901d5d63830cb6a69a707819f4d958ae5ecb955d8a5ad823a2e44"}, + {file = "tokenizers-0.14.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d06a6fe406df1e616f9e649522683411c6c345ddaaaad7e50bbb60a2cb27e04d"}, + {file = "tokenizers-0.14.0-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6e2d4bc223dc6a99efbe9266242f1ac03eb0bef0104e6cef9f9512dd5c816b"}, + {file = "tokenizers-0.14.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08ea1f612796e438c9a7e2ad86ab3c1c05c8fe0fad32fcab152c69a3a1a90a86"}, + {file = "tokenizers-0.14.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ab1a58c05a3bd8ece95eb5d1bc909b3fb11acbd3ff514e3cbd1669e3ed28f5b"}, + {file = "tokenizers-0.14.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:495dc7d3b78815de79dafe7abce048a76154dadb0ffc7f09b7247738557e5cef"}, + {file = "tokenizers-0.14.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aaa0401a245d891b3b2ba9cf027dc65ca07627e11fe3ce597644add7d07064f8"}, + {file = "tokenizers-0.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae4fa13a786fd0d6549da241c6a1077f9b6320a7120d922ccc201ad1d4feea8f"}, + {file = "tokenizers-0.14.0-cp37-none-win32.whl", hash = "sha256:ae0d5b5ab6032c24a2e74cc15f65b6510070926671129e922aa3826c834558d7"}, + {file = "tokenizers-0.14.0-cp37-none-win_amd64.whl", hash = "sha256:2839369a9eb948905612f5d8e70453267d9c7bf17573e5ab49c2f28368fd635d"}, + {file = "tokenizers-0.14.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:f483af09a07fcb8b8b4cd07ac1be9f58bb739704ef9156e955531299ab17ec75"}, + {file = "tokenizers-0.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9c2ec661d0d63e618cb145ad15ddb6a81e16d9deb7a203f385d78141da028984"}, + {file = "tokenizers-0.14.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:97e87eb7cbeff63c3b1aa770fdcf18ea4f1c852bfb75d0c913e71b8924a99d61"}, + {file = "tokenizers-0.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98c4bd09b47f77f41785488971543de63db82608f0dc0bc6646c876b5ca44d1f"}, + {file = "tokenizers-0.14.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0cbeb5406be31f7605d032bb261f2e728da8ac1f4f196c003bc640279ceb0f52"}, + {file = "tokenizers-0.14.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe799fa48fd7dd549a68abb7bee32dd3721f50210ad2e3e55058080158c72c25"}, + {file = "tokenizers-0.14.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:66daf7c6375a95970e86cb3febc48becfeec4e38b2e0195218d348d3bb86593b"}, + {file = "tokenizers-0.14.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b177422af79a77c46bb8f56d73827e688fdc092878cff54e24f5c07a908db"}, + {file = "tokenizers-0.14.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9aef7a5622648b70f979e96cbc2f795eba5b28987dd62f4dbf8f1eac6d64a1a"}, + {file = "tokenizers-0.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:397a24feff284d39b40fdd61c1c828bb6648dfe97b6766c84fbaf7256e272d09"}, + {file = "tokenizers-0.14.0-cp38-none-win32.whl", hash = "sha256:93cc2ec19b6ff6149b2e5127ceda3117cc187dd38556a1ed93baba13dffda069"}, + {file = "tokenizers-0.14.0-cp38-none-win_amd64.whl", hash = "sha256:bf7f540ab8a6fc53fb762963edb7539b11f00af8f70b206f0a6d1a25109ad307"}, + {file = "tokenizers-0.14.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a58d0b34586f4c5229de5aa124cf76b9455f2e01dc5bd6ed018f6e3bb12572d3"}, + {file = "tokenizers-0.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:90ceca6a06bb4b0048d0a51d0d47ef250d3cb37cc36b6b43334be8c02ac18b0f"}, + {file = "tokenizers-0.14.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5f6c9554bda64799b1d65052d834553bff9a6ef4a6c2114668e2ed8f1871a2a3"}, + {file = "tokenizers-0.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee14b41024bc05ea172fc2c87f66b60d7c5c636c3a52a09a25ec18e752e6dc7"}, + {file = "tokenizers-0.14.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:879201b1c76b24dc70ce02fc42c3eeb7ff20c353ce0ee638be6449f7c80e73ba"}, + {file = "tokenizers-0.14.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca79ea6ddde5bb32f7ad1c51de1032829c531e76bbcae58fb3ed105a31faf021"}, + {file = "tokenizers-0.14.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd5934048e60aedddf6c5b076d44ccb388702e1650e2eb7b325a1682d883fbf9"}, + {file = "tokenizers-0.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a1566cabd4bf8f09d6c1fa7a3380a181801a495e7218289dbbd0929de471711"}, + {file = "tokenizers-0.14.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a8fc72a7adc6fa12db38100c403d659bc01fbf6e57f2cc9219e75c4eb0ea313c"}, + {file = "tokenizers-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7fd08ed6c14aa285482d9e5f48c04de52bdbcecaca0d30465d7a36bbea6b14df"}, + {file = "tokenizers-0.14.0-cp39-none-win32.whl", hash = "sha256:3279c0c1d5fdea7d3499c582fed392fb0463d1046544ca010f53aeee5d2ce12c"}, + {file = "tokenizers-0.14.0-cp39-none-win_amd64.whl", hash = "sha256:203ca081d25eb6e4bc72ea04d552e457079c5c6a3713715ece246f6ca02ca8d0"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:b45704d5175499387e33a1dd5c8d49ab4d7ef3c36a9ba8a410bb3e68d10f80a0"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6d17d5eb38ccc2f615a7a3692dfa285abe22a1e6d73bbfd753599e34ceee511c"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a7e6e7989ba77a20c33f7a8a45e0f5b3e7530b2deddad2c3b2a58b323156134"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81876cefea043963abf6c92e0cf73ce6ee10bdc43245b6565ce82c0305c2e613"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d8cd05f73d1ce875a23bfdb3a572417c0f46927c6070ca43a7f6f044c3d6605"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:419a38b89be0081d872eac09449c03cd6589c2ee47461184592ee4b1ad93af1d"}, + {file = "tokenizers-0.14.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4caf274a9ba944eb83bc695beef95abe24ce112907fb06217875894d8a4f62b8"}, + {file = "tokenizers-0.14.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:6ecb3a7741d7ebf65db93d246b102efca112860707e07233f1b88703cb01dbc5"}, + {file = "tokenizers-0.14.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cb7fe9a383cb2932848e459d0277a681d58ad31aa6ccda204468a8d130a9105c"}, + {file = "tokenizers-0.14.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4731e0577780d85788ab4f00d54e16e76fe305739396e6fb4c54b89e6fa12de"}, + {file = "tokenizers-0.14.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9900291ccd19417128e328a26672390365dab1d230cd00ee7a5e2a0319e2716"}, + {file = "tokenizers-0.14.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:493e6932fbca6875fd2e51958f1108ce4c5ae41aa6f2b8017c5f07beaff0a1ac"}, + {file = "tokenizers-0.14.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1792e6b46b89aba0d501c0497f38c96e5b54735379fd8a07a28f45736ba51bb1"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0af26d37c7080688ef606679f3a3d44b63b881de9fa00cc45adc240ba443fd85"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:99379ec4d7023c07baed85c68983bfad35fd210dfbc256eaafeb842df7f888e3"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:84118aa60dcbb2686730342a0cb37e54e02fde001f936557223d46b6cd8112cd"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d616e1859ffcc8fcda60f556c34338b96fb72ca642f6dafc3b1d2aa1812fb4dd"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7826b79bbbffc2150bf8d621297cc600d8a1ea53992547c4fd39630de10466b4"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eb3931d734f1e66b77c2a8e22ebe0c196f127c7a0f48bf9601720a6f85917926"}, + {file = "tokenizers-0.14.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6a475b5cafc7a740bf33d00334b1f2b434b6124198384d8b511931a891be39ff"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3d3c9e286ae00b0308903d2ef7b31efc84358109aa41abaa27bd715401c3fef4"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:27244e96810434cf705f317e9b74a1163cd2be20bdbd3ed6b96dae1914a6778c"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ca9b0536fd5f03f62427230e85d9d57f9eed644ab74c319ae4877c9144356aed"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f64cdff8c0454295b739d77e25cff7264fa9822296395e60cbfecc7f66d88fb"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a00cdfb40544656b7a3b176049d63227d5e53cf2574912514ebb4b9da976aaa1"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b611d96b96957cb2f39560c77cc35d2fcb28c13d5b7d741412e0edfdb6f670a8"}, + {file = "tokenizers-0.14.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:27ad1c02fdd74dcf3502fafb87393412e65f698f2e3aba4ad568a1f3b43d5c9f"}, + {file = "tokenizers-0.14.0.tar.gz", hash = "sha256:a06efa1f19dcc0e9bd0f4ffbf963cb0217af92a9694f68fe7eee5e1c6ddc4bde"}, ] +[package.dependencies] +huggingface_hub = ">=0.16.4,<0.17" + [package.extras] -dev = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] -docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +dev = ["tokenizers[testing]"] +docs = ["setuptools_rust", "sphinx", "sphinx_rtd_theme"] testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] [[package]] @@ -7516,87 +7566,80 @@ telegram = ["requests"] [[package]] name = "traitlets" -version = "5.9.0" +version = "5.10.0" description = "Traitlets Python configuration system" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, - {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, + {file = "traitlets-5.10.0-py3-none-any.whl", hash = "sha256:417745a96681fbb358e723d5346a547521f36e9bd0d50ba7ab368fff5d67aa54"}, + {file = "traitlets-5.10.0.tar.gz", hash = "sha256:f584ea209240466e66e91f3c81aa7d004ba4cf794990b0c775938a1544217cd1"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.5.1)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] [[package]] name = "transformers" -version = "4.33.0" -description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +version = "4.17.0" +description = "State-of-the-art Natural Language Processing for TensorFlow 2.0 and PyTorch" optional = true -python-versions = ">=3.8.0" +python-versions = ">=3.6.0" files = [ - {file = "transformers-4.33.0-py3-none-any.whl", hash = "sha256:c3b7f818e90c4361bb50ad541ab94e28329aa0d97a1c45ffafa5a8b693bc73ec"}, - {file = "transformers-4.33.0.tar.gz", hash = "sha256:9e894dc62cfb02d92c1201e57219765b53be6486a306db8b60414e93fc2e39a5"}, + {file = "transformers-4.17.0-py3-none-any.whl", hash = "sha256:5c7d1955693ebf4a69a0fa700b2ef730232d5d7c1528e15d44c1d473b38f57b8"}, + {file = "transformers-4.17.0.tar.gz", hash = "sha256:986fd59255460555b893a2b1827b9b8dd4e5cd6343e4409d18539208f69fb51b"}, ] [package.dependencies] filelock = "*" -huggingface-hub = ">=0.15.1,<1.0" +huggingface-hub = ">=0.1.0,<1.0" numpy = ">=1.17" packaging = ">=20.0" pyyaml = ">=5.1" regex = "!=2019.12.17" requests = "*" -safetensors = ">=0.3.1" -tokenizers = ">=0.11.1,<0.11.3 || >0.11.3,<0.14" +sacremoses = "*" +tokenizers = ">=0.11.1,<0.11.3 || >0.11.3" tqdm = ">=4.27" [package.extras] -accelerate = ["accelerate (>=0.20.3)"] -agents = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch (>=1.10,!=1.12.0)"] -all = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.15)", "tensorflow-text (<2.15)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.10,!=1.12.0)", "torchaudio", "torchvision"] -audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +all = ["Pillow", "codecarbon (==1.2.0)", "flax (>=0.3.5)", "jax (>=0.2.8)", "jaxlib (>=0.1.65)", "librosa", "onnxconverter-common", "optax (>=0.0.8)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.3.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.3)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3)", "torch (>=1.0)", "torchaudio"] +audio = ["librosa", "phonemizer", "pyctcdecode (>=0.3.0)"] codecarbon = ["codecarbon (==1.2.0)"] -deepspeed = ["accelerate (>=0.20.3)", "deepspeed (>=0.9.3)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.20.3)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "optuna", "parameterized", "protobuf", "psutil", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorflow (>=2.6,<2.15)", "tensorflow-text (<2.15)", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.10,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>=2.6,<2.15)", "tensorflow-text (<2.15)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "urllib3 (<2.0.0)"] -dev-torch = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "accelerate (>=0.20.3)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.10,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -docs = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "hf-doc-builder", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.15)", "tensorflow-text (<2.15)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.10,!=1.12.0)", "torchaudio", "torchvision"] -docs-specific = ["hf-doc-builder"] +deepspeed = ["deepspeed (>=0.5.9)"] +dev = ["GitPython (<3.1.19)", "Pillow", "black (>=22.0,<23.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.2)", "datasets", "faiss-cpu", "flake8 (>=3.8.3)", "flax (>=0.3.5)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.2.8)", "jaxlib (>=0.1.65)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.3.0)", "pytest", "pytest-timeout", "pytest-xdist", "ray[tune]", "rouge-score", "sacrebleu (>=1.4.12,<2.0.0)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.3)", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3)", "torch (>=1.0)", "torchaudio", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +dev-tensorflow = ["GitPython (<3.1.19)", "Pillow", "black (>=22.0,<23.0)", "cookiecutter (==1.7.2)", "datasets", "faiss-cpu", "flake8 (>=3.8.3)", "isort (>=5.5.4)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.3.0)", "pytest", "pytest-timeout", "pytest-xdist", "rouge-score", "sacrebleu (>=1.4.12,<2.0.0)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>=2.3)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.11.1,!=0.11.3)"] +dev-torch = ["GitPython (<3.1.19)", "Pillow", "black (>=22.0,<23.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.2)", "datasets", "faiss-cpu", "flake8 (>=3.8.3)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.3.0)", "pytest", "pytest-timeout", "pytest-xdist", "ray[tune]", "rouge-score", "sacrebleu (>=1.4.12,<2.0.0)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3)", "torch (>=1.0)", "torchaudio", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +docs = ["Pillow", "codecarbon (==1.2.0)", "flax (>=0.3.5)", "jax (>=0.2.8)", "jaxlib (>=0.1.65)", "librosa", "onnxconverter-common", "optax (>=0.0.8)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.3.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.3)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3)", "torch (>=1.0)", "torchaudio"] fairscale = ["fairscale (>0.3)"] -flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)"] -flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +flax = ["flax (>=0.3.5)", "jax (>=0.2.8)", "jaxlib (>=0.1.65)", "optax (>=0.0.8)"] +flax-speech = ["librosa", "phonemizer", "pyctcdecode (>=0.3.0)"] ftfy = ["ftfy"] integrations = ["optuna", "ray[tune]", "sigopt"] -ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] -modelcreation = ["cookiecutter (==1.7.3)"] -natten = ["natten (>=0.14.6)"] +ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] +modelcreation = ["cookiecutter (==1.7.2)"] onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] optuna = ["optuna"] -quality = ["GitPython (<3.1.19)", "black (>=23.1,<24.0)", "datasets (!=2.5.0)", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "ruff (>=0.0.241,<=0.0.259)", "urllib3 (<2.0.0)"] +quality = ["GitPython (<3.1.19)", "black (>=22.0,<23.0)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] ray = ["ray[tune]"] -retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] +retrieval = ["datasets", "faiss-cpu"] sagemaker = ["sagemaker (>=2.31.0)"] sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] -serving = ["fastapi", "pydantic (<2)", "starlette", "uvicorn"] +serving = ["fastapi", "pydantic", "starlette", "uvicorn"] sigopt = ["sigopt"] sklearn = ["scikit-learn"] -speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "parameterized", "protobuf", "psutil", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "timeout-decorator"] -tf = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow (>=2.6,<2.15)", "tensorflow-text (<2.15)", "tf2onnx"] -tf-cpu = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow-cpu (>=2.6,<2.15)", "tensorflow-text (<2.15)", "tf2onnx"] -tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] +speech = ["librosa", "phonemizer", "pyctcdecode (>=0.3.0)", "torchaudio"] +testing = ["GitPython (<3.1.19)", "black (>=22.0,<23.0)", "cookiecutter (==1.7.2)", "datasets", "faiss-cpu", "nltk", "parameterized", "psutil", "pytest", "pytest-timeout", "pytest-xdist", "rouge-score", "sacrebleu (>=1.4.12,<2.0.0)", "timeout-decorator"] +tf = ["onnxconverter-common", "tensorflow (>=2.3)", "tf2onnx"] +tf-cpu = ["onnxconverter-common", "tensorflow-cpu (>=2.3)", "tf2onnx"] +tf-speech = ["librosa", "phonemizer", "pyctcdecode (>=0.3.0)"] timm = ["timm"] -tokenizers = ["tokenizers (>=0.11.1,!=0.11.3,<0.14)"] -torch = ["accelerate (>=0.20.3)", "torch (>=1.10,!=1.12.0)"] -torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -torch-vision = ["Pillow (<10.0.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.15.1,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.10,!=1.12.0)", "tqdm (>=4.27)"] -video = ["av (==9.2.0)", "decord (==0.6.0)"] -vision = ["Pillow (<10.0.0)"] +tokenizers = ["tokenizers (>=0.11.1,!=0.11.3)"] +torch = ["torch (>=1.0)"] +torch-speech = ["librosa", "phonemizer", "pyctcdecode (>=0.3.0)", "torchaudio"] +torchhub = ["filelock", "huggingface-hub (>=0.1.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.11.1,!=0.11.3)", "torch (>=1.0)", "tqdm (>=4.27)"] +vision = ["Pillow"] [[package]] name = "twine" @@ -7760,13 +7803,13 @@ files = [ [[package]] name = "types-redis" -version = "4.6.0.5" +version = "4.6.0.6" description = "Typing stubs for redis" optional = false python-versions = "*" files = [ - {file = "types-redis-4.6.0.5.tar.gz", hash = "sha256:5f179d10bd3ca995a8134aafcddfc3e12d52b208437c4529ef27e68acb301f38"}, - {file = "types_redis-4.6.0.5-py3-none-any.whl", hash = "sha256:4f662060247a2363c7a8f0b7e52915d68960870ff16a749a891eabcf87ed0be4"}, + {file = "types-redis-4.6.0.6.tar.gz", hash = "sha256:7865a843802937ab2ddca33579c4e255bfe73f87af85824ead7a6729ba92fc52"}, + {file = "types_redis-4.6.0.6-py3-none-any.whl", hash = "sha256:e0e9dcc530623db3a41ec058ccefdcd5c7582557f02ab5f7aa9a27fe10a78d7e"}, ] [package.dependencies] @@ -7994,15 +8037,26 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my [[package]] name = "validators" -version = "0.21.0" +version = "0.22.0" description = "Python Data Validation for Humans™" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.8" files = [ - {file = "validators-0.21.0-py3-none-any.whl", hash = "sha256:3470db6f2384c49727ee319afa2e97aec3f8fad736faa6067e0fd7f9eaf2c551"}, - {file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"}, + {file = "validators-0.22.0-py3-none-any.whl", hash = "sha256:61cf7d4a62bbae559f2e54aed3b000cea9ff3e2fdbe463f51179b92c58c9585a"}, + {file = "validators-0.22.0.tar.gz", hash = "sha256:77b2689b172eeeb600d9605ab86194641670cdb73b60afd577142a9397873370"}, ] +[package.extras] +docs-offline = ["myst-parser (>=2.0.0)", "pypandoc-binary (>=1.11)", "sphinx (>=7.1.1)"] +docs-online = ["mkdocs (>=1.5.2)", "mkdocs-git-revision-date-localized-plugin (>=1.2.0)", "mkdocs-material (>=9.2.6)", "mkdocstrings[python] (>=0.22.0)", "pyaml (>=23.7.0)"] +hooks = ["pre-commit (>=3.3.3)"] +package = ["build (>=1.0.0)", "twine (>=4.0.2)"] +runner = ["tox (>=4.11.1)"] +sast = ["bandit[toml] (>=1.7.5)"] +testing = ["pytest (>=7.4.0)"] +tooling = ["black (>=23.7.0)", "pyright (>=1.1.325)", "ruff (>=0.0.287)"] +tooling-extras = ["pyaml (>=23.7.0)", "pypandoc-binary (>=1.11)", "pytest (>=7.4.0)"] + [[package]] name = "vine" version = "5.0.0" @@ -8061,44 +8115,32 @@ files = [ [[package]] name = "weaviate-client" -version = "3.23.2" +version = "3.24.1" description = "A python native Weaviate client" optional = false python-versions = ">=3.8" files = [ - {file = "weaviate-client-3.23.2.tar.gz", hash = "sha256:1c8c94df032dd2fa5a4ea615fc69ccb983ffad5cc02974f78c793839e61ac150"}, - {file = "weaviate_client-3.23.2-py3-none-any.whl", hash = "sha256:88ffc38cca07806d64726cc74bc194c7da50b222aa4e2cd129f4c1f5e53e9b61"}, + {file = "weaviate-client-3.24.1.tar.gz", hash = "sha256:e073350c21bd4dca5c0eac5dd5d95fb474278c715aaf2041e382d86bb7518ac8"}, + {file = "weaviate_client-3.24.1-py3-none-any.whl", hash = "sha256:2179ea1093685f6f7cefbd19e2896eeb4b4c720954d953018c0853f775cdad8b"}, ] [package.dependencies] -authlib = ">=1.1.0" -requests = ">=2.28.0,<=2.31.0" -tqdm = ">=4.59.0,<5.0.0" -validators = ">=0.18.2,<=0.21.0" +authlib = ">=1.2.1,<2.0.0" +requests = ">=2.30.0,<3.0.0" +validators = ">=0.21.2,<1.0.0" [package.extras] -grpc = ["grpcio", "grpcio-tools"] - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -optional = false -python-versions = "*" -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] +grpc = ["grpcio (>=1.57.0,<2.0.0)", "grpcio-tools (>=1.57.0,<2.0.0)"] [[package]] name = "websocket-client" -version = "1.6.2" +version = "1.6.3" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" files = [ - {file = "websocket-client-1.6.2.tar.gz", hash = "sha256:53e95c826bf800c4c465f50093a8c4ff091c7327023b10bfaff40cf1ef170eaa"}, - {file = "websocket_client-1.6.2-py3-none-any.whl", hash = "sha256:ce54f419dfae71f4bdba69ebe65bf7f0a93fe71bc009ad3a010aacc3eebad537"}, + {file = "websocket-client-1.6.3.tar.gz", hash = "sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f"}, + {file = "websocket_client-1.6.3-py3-none-any.whl", hash = "sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03"}, ] [package.extras] @@ -8345,13 +8387,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "xlsxwriter" -version = "3.1.2" +version = "3.1.3" description = "A Python module for creating Excel XLSX files." optional = false python-versions = ">=3.6" files = [ - {file = "XlsxWriter-3.1.2-py3-none-any.whl", hash = "sha256:331508ff39d610ecdaf979e458840bc1eab6e6a02cfd5d08f044f0f73636236f"}, - {file = "XlsxWriter-3.1.2.tar.gz", hash = "sha256:78751099a770273f1c98b8d6643351f68f98ae8e6acf9d09d37dc6798f8cd3de"}, + {file = "XlsxWriter-3.1.3-py3-none-any.whl", hash = "sha256:b1fde9b60dc25fff589f3baf58e95178048ba87a7916d45f6e3bce4a61f56e64"}, + {file = "XlsxWriter-3.1.3.tar.gz", hash = "sha256:696c16458d50712a3a5f902676c045ddb4c1bd1b54595f501e582d549abdbdb9"}, ] [[package]] @@ -8587,4 +8629,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "269c762dad4859168a868eec9eacce6d930e0184f27c07069458f2e667d46a98" +content-hash = "3751ee3efd625b14a7b6cbd6200565a65b5ecd369761aff0537f5f480b899619" diff --git a/pyproject.toml b/pyproject.toml index 70f5723f4..1b0055752 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ typer = "^0.9.0" gunicorn = "^21.1.0" langchain = "^0.0.274" openai = "^0.27.8" -pandas = "^2.0.0" +pandas = "2.0.3" chromadb = "^0.3.21" huggingface-hub = { version = "^0.16.0", extras = ["inference"] } rich = "^13.4.2" From 6871cc5ab68fa0383534e7421c605c1f53b3ff71 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:34:46 -0300 Subject: [PATCH 141/213] adds vector store tests --- tests/conftest.py | 24 +- tests/data/Vector_store.json | 1283 ++++++++++++++++++++++++++++++++++ tests/test_endpoints.py | 51 ++ 3 files changed, 1357 insertions(+), 1 deletion(-) create mode 100644 tests/data/Vector_store.json diff --git a/tests/conftest.py b/tests/conftest.py index 61ac84d06..aeaef940a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,7 +33,9 @@ def pytest_configure(): pytest.BASIC_CHAT_WITH_PROMPT_AND_HISTORY = ( Path(__file__).parent.absolute() / "data" / "BasicChatwithPromptandHistory.json" ) - + pytest.VECTOR_STORE_PATH = ( + Path(__file__).parent.absolute() / "data" / "Vector_store.json" + ) pytest.CODE_WITH_SYNTAX_ERROR = """ def get_text(): retun "Hello World" @@ -111,6 +113,12 @@ def json_flow_with_prompt_and_history(): return f.read() +@pytest.fixture +def json_vector_store(): + with open(pytest.VECTOR_STORE_PATH, "r") as f: + return f.read() + + @pytest.fixture(name="session") def session_fixture(): engine = create_engine( @@ -230,3 +238,17 @@ def added_flow(client, json_flow_with_prompt_and_history, logged_in_headers): assert response.json()["name"] == flow.name assert response.json()["data"] == flow.data return response.json() + + +@pytest.fixture +def added_vector_store(client, json_vector_store, logged_in_headers): + vector_store = orjson.loads(json_vector_store) + data = vector_store["data"] + vector_store = FlowCreate(name="Vector Store", description="description", data=data) + response = client.post( + "api/v1/flows/", json=vector_store.dict(), headers=logged_in_headers + ) + assert response.status_code == 201 + assert response.json()["name"] == vector_store.name + assert response.json()["data"] == vector_store.data + return response.json() diff --git a/tests/data/Vector_store.json b/tests/data/Vector_store.json new file mode 100644 index 000000000..2a1ddd5f3 --- /dev/null +++ b/tests/data/Vector_store.json @@ -0,0 +1,1283 @@ +{ + "name": "Vector Store", + "description": "An agent that can query a Vector Store.\nTry asking \"How do I upload examples to Langflow?\"\n\n\n\n", + "data": { + "nodes": [ + { + "width": 384, + "height": 267, + "id": "VectorStoreAgent-FOmxY", + "type": "genericNode", + "position": { + "x": 2115.5183674856203, + "y": -1277.6284872455249 + }, + "data": { + "type": "VectorStoreAgent", + "node": { + "template": { + "llm": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "llm", + "display_name": "LLM", + "advanced": false, + "dynamic": false, + "info": "", + "type": "BaseLanguageModel", + "list": false + }, + "vectorstoreinfo": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "vectorstoreinfo", + "display_name": "Vector Store Info", + "advanced": false, + "dynamic": false, + "info": "", + "type": "VectorStoreInfo", + "list": false + }, + "_type": "vectorstore_agent" + }, + "description": "Construct an agent from a Vector Store.", + "base_classes": [ + "AgentExecutor" + ], + "display_name": "VectorStoreAgent", + "documentation": "" + }, + "id": "VectorStoreAgent-FOmxY", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 2115.5183674856203, + "y": -1277.6284872455249 + }, + "dragging": false + }, + { + "width": 384, + "height": 399, + "id": "VectorStoreInfo-z0sH5", + "type": "genericNode", + "position": { + "x": 1553.2875394928135, + "y": -1319.2113273706286 + }, + "data": { + "type": "VectorStoreInfo", + "node": { + "template": { + "vectorstore": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "vectorstore", + "advanced": false, + "dynamic": false, + "info": "", + "type": "VectorStore", + "list": false + }, + "description": { + "required": true, + "placeholder": "", + "show": true, + "multiline": true, + "password": false, + "name": "description", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false, + "value": "Instructions to upload examples to Langflow Community Examples" + }, + "name": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "name", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false, + "value": "UploadExamples" + }, + "_type": "VectorStoreInfo" + }, + "description": "Information about a VectorStore.", + "base_classes": [ + "VectorStoreInfo" + ], + "display_name": "VectorStoreInfo", + "documentation": "" + }, + "id": "VectorStoreInfo-z0sH5", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 1553.2875394928135, + "y": -1319.2113273706286 + }, + "dragging": false + }, + { + "width": 384, + "height": 359, + "id": "OpenAIEmbeddings-lge2J", + "type": "genericNode", + "position": { + "x": 677.2699276778915, + "y": -734.4639958173494 + }, + "data": { + "type": "OpenAIEmbeddings", + "node": { + "template": { + "allowed_special": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": [], + "password": false, + "name": "allowed_special", + "advanced": true, + "dynamic": false, + "info": "", + "type": "Literal'all'", + "list": true + }, + "disallowed_special": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "all", + "password": false, + "name": "disallowed_special", + "advanced": true, + "dynamic": false, + "info": "", + "type": "Literal'all'", + "list": true + }, + "chunk_size": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": 1000, + "password": false, + "name": "chunk_size", + "advanced": true, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "client": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "client", + "advanced": true, + "dynamic": false, + "info": "", + "type": "Any", + "list": false + }, + "deployment": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "text-embedding-ada-002", + "password": false, + "name": "deployment", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "embedding_ctx_length": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": 8191, + "password": false, + "name": "embedding_ctx_length", + "advanced": true, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "headers": { + "required": false, + "placeholder": "", + "show": false, + "multiline": true, + "value": "{'Authorization':\n 'Bearer '}", + "password": false, + "name": "headers", + "advanced": true, + "dynamic": false, + "info": "", + "type": "Any", + "list": false + }, + "max_retries": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": 6, + "password": false, + "name": "max_retries", + "advanced": true, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "model": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "text-embedding-ada-002", + "password": false, + "name": "model", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "model_kwargs": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "model_kwargs", + "advanced": true, + "dynamic": false, + "info": "", + "type": "code", + "list": false + }, + "openai_api_base": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": true, + "name": "openai_api_base", + "display_name": "OpenAI API Base", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false, + "value": "" + }, + "openai_api_key": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "", + "password": true, + "name": "openai_api_key", + "display_name": "OpenAI API Key", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "openai_api_type": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": true, + "name": "openai_api_type", + "display_name": "OpenAI API Type", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false, + "value": "" + }, + "openai_api_version": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": true, + "name": "openai_api_version", + "display_name": "OpenAI API Version", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false, + "value": "" + }, + "openai_organization": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "openai_organization", + "display_name": "OpenAI Organization", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "openai_proxy": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "openai_proxy", + "display_name": "OpenAI Proxy", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "request_timeout": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "request_timeout", + "advanced": true, + "dynamic": false, + "info": "", + "type": "float", + "list": false + }, + "show_progress_bar": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": false, + "password": false, + "name": "show_progress_bar", + "advanced": true, + "dynamic": false, + "info": "", + "type": "bool", + "list": false + }, + "tiktoken_model_name": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": true, + "name": "tiktoken_model_name", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false, + "value": "" + }, + "_type": "OpenAIEmbeddings" + }, + "description": "OpenAI embedding models.", + "base_classes": [ + "OpenAIEmbeddings", + "Embeddings" + ], + "display_name": "OpenAIEmbeddings", + "documentation": "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai" + }, + "id": "OpenAIEmbeddings-lge2J", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 677.2699276778915, + "y": -734.4639958173494 + }, + "dragging": false + }, + { + "width": 384, + "height": 515, + "id": "Chroma-UK4a8", + "type": "genericNode", + "position": { + "x": 1138.12587416446, + "y": -1289.1517285671812 + }, + "data": { + "type": "Chroma", + "node": { + "template": { + "client": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "client", + "advanced": false, + "dynamic": false, + "info": "", + "type": "chromadb.Client", + "list": false + }, + "client_settings": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "client_settings", + "advanced": false, + "dynamic": false, + "info": "", + "type": "chromadb.config.Setting", + "list": true + }, + "documents": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "documents", + "display_name": "Documents", + "advanced": false, + "dynamic": false, + "info": "", + "type": "Document", + "list": true + }, + "embedding": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "embedding", + "display_name": "Embedding", + "advanced": false, + "dynamic": false, + "info": "", + "type": "Embeddings", + "list": false + }, + "chroma_server_cors_allow_origins": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "chroma_server_cors_allow_origins", + "display_name": "Chroma Server CORS Allow Origins", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": true + }, + "chroma_server_grpc_port": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "chroma_server_grpc_port", + "display_name": "Chroma Server GRPC Port", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "chroma_server_host": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "chroma_server_host", + "display_name": "Chroma Server Host", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "chroma_server_http_port": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "chroma_server_http_port", + "display_name": "Chroma Server HTTP Port", + "advanced": true, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "chroma_server_ssl_enabled": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": false, + "password": false, + "name": "chroma_server_ssl_enabled", + "display_name": "Chroma Server SSL Enabled", + "advanced": true, + "dynamic": false, + "info": "", + "type": "bool", + "list": false + }, + "collection_metadata": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "collection_metadata", + "advanced": false, + "dynamic": false, + "info": "", + "type": "code", + "list": false + }, + "collection_name": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "langflow", + "password": false, + "name": "collection_name", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "ids": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "ids", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": true + }, + "metadatas": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "metadatas", + "advanced": false, + "dynamic": false, + "info": "", + "type": "code", + "list": true + }, + "persist": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": false, + "password": false, + "name": "persist", + "display_name": "Persist", + "advanced": false, + "dynamic": false, + "info": "", + "type": "bool", + "list": false + }, + "persist_directory": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "persist_directory", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "search_kwargs": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "{}", + "password": false, + "name": "search_kwargs", + "advanced": true, + "dynamic": false, + "info": "", + "type": "code", + "list": false + }, + "_type": "Chroma" + }, + "description": "Create a Chroma vectorstore from a raw documents.", + "base_classes": [ + "VectorStore", + "Chroma", + "BaseRetriever", + "VectorStoreRetriever" + ], + "display_name": "Chroma", + "custom_fields": {}, + "output_types": [], + "documentation": "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma" + }, + "id": "Chroma-UK4a8", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 1138.12587416446, + "y": -1289.1517285671812 + }, + "dragging": false + }, + { + "width": 384, + "height": 575, + "id": "RecursiveCharacterTextSplitter-AUWrU", + "type": "genericNode", + "position": { + "x": 607.3861456929772, + "y": -1343.8126308350086 + }, + "data": { + "type": "RecursiveCharacterTextSplitter", + "node": { + "template": { + "documents": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "documents", + "advanced": false, + "dynamic": false, + "info": "", + "type": "Document", + "list": true + }, + "chunk_overlap": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "value": 200, + "password": false, + "name": "chunk_overlap", + "display_name": "Chunk Overlap", + "advanced": false, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "chunk_size": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "value": 1000, + "password": false, + "name": "chunk_size", + "display_name": "Chunk Size", + "advanced": false, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "separator_type": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "value": "Text", + "password": false, + "options": [ + "Text", + "cpp", + "go", + "html", + "java", + "js", + "latex", + "markdown", + "php", + "proto", + "python", + "rst", + "ruby", + "rust", + "scala", + "sol", + "swift" + ], + "name": "separator_type", + "display_name": "Separator Type", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": true + }, + "separators": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "value": ".", + "password": false, + "name": "separators", + "display_name": "Separator", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "_type": "RecursiveCharacterTextSplitter" + }, + "description": "Splitting text by recursively look at characters.", + "base_classes": [ + "Document" + ], + "display_name": "RecursiveCharacterTextSplitter", + "custom_fields": {}, + "output_types": [ + "Document" + ], + "documentation": "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter" + }, + "id": "RecursiveCharacterTextSplitter-AUWrU", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 607.3861456929772, + "y": -1343.8126308350086 + } + }, + { + "width": 384, + "height": 379, + "id": "WebBaseLoader-aUAEE", + "type": "genericNode", + "position": { + "x": 60.77712301470575, + "y": -1345.575885746874 + }, + "data": { + "type": "WebBaseLoader", + "node": { + "template": { + "metadata": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "value": "{}", + "password": false, + "name": "metadata", + "display_name": "Metadata", + "advanced": false, + "dynamic": false, + "info": "", + "type": "code", + "list": false + }, + "web_path": { + "required": true, + "placeholder": "", + "show": true, + "multiline": false, + "value": "http://docs.langflow.org/examples/how-upload-examples", + "password": false, + "name": "web_path", + "display_name": "Web Page", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "_type": "WebBaseLoader" + }, + "description": "Load HTML pages using `urllib` and parse them with `BeautifulSoup'.", + "base_classes": [ + "Document" + ], + "display_name": "WebBaseLoader", + "custom_fields": {}, + "output_types": [ + "Document" + ], + "documentation": "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/web_base" + }, + "id": "WebBaseLoader-aUAEE", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 60.77712301470575, + "y": -1345.575885746874 + }, + "dragging": false + }, + { + "width": 384, + "height": 621, + "id": "ChatOpenAI-U4mZ2", + "type": "genericNode", + "position": { + "x": 1557.7805431884235, + "y": -897.7091381330642 + }, + "data": { + "type": "ChatOpenAI", + "node": { + "template": { + "callbacks": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "callbacks", + "advanced": false, + "dynamic": false, + "info": "", + "type": "langchain.callbacks.base.BaseCallbackHandler", + "list": true + }, + "cache": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "cache", + "advanced": false, + "dynamic": false, + "info": "", + "type": "bool", + "list": false + }, + "client": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "client", + "advanced": false, + "dynamic": false, + "info": "", + "type": "Any", + "list": false + }, + "max_retries": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "value": 6, + "password": false, + "name": "max_retries", + "advanced": false, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "max_tokens": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": true, + "name": "max_tokens", + "advanced": false, + "dynamic": false, + "info": "", + "type": "int", + "list": false, + "value": "" + }, + "metadata": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "metadata", + "advanced": false, + "dynamic": false, + "info": "", + "type": "code", + "list": false + }, + "model_kwargs": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "model_kwargs", + "advanced": true, + "dynamic": false, + "info": "", + "type": "code", + "list": false + }, + "model_name": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "gpt-3.5-turbo-0613", + "password": false, + "options": [ + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k-0613", + "gpt-3.5-turbo-16k", + "gpt-4-0613", + "gpt-4-32k-0613", + "gpt-4", + "gpt-4-32k" + ], + "name": "model_name", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": true + }, + "n": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "value": 1, + "password": false, + "name": "n", + "advanced": false, + "dynamic": false, + "info": "", + "type": "int", + "list": false + }, + "openai_api_base": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "password": false, + "name": "openai_api_base", + "display_name": "OpenAI API Base", + "advanced": false, + "dynamic": false, + "info": "\nThe base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.\n", + "type": "str", + "list": false + }, + "openai_api_key": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "", + "password": true, + "name": "openai_api_key", + "display_name": "OpenAI API Key", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "openai_organization": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "openai_organization", + "display_name": "OpenAI Organization", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "openai_proxy": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "openai_proxy", + "display_name": "OpenAI Proxy", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "request_timeout": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "request_timeout", + "advanced": false, + "dynamic": false, + "info": "", + "type": "float", + "list": false + }, + "streaming": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "value": false, + "password": false, + "name": "streaming", + "advanced": false, + "dynamic": false, + "info": "", + "type": "bool", + "list": false + }, + "tags": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "tags", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": true + }, + "temperature": { + "required": false, + "placeholder": "", + "show": true, + "multiline": false, + "value": "0.2", + "password": false, + "name": "temperature", + "advanced": false, + "dynamic": false, + "info": "", + "type": "float", + "list": false + }, + "tiktoken_model_name": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "password": false, + "name": "tiktoken_model_name", + "advanced": false, + "dynamic": false, + "info": "", + "type": "str", + "list": false + }, + "verbose": { + "required": false, + "placeholder": "", + "show": false, + "multiline": false, + "value": false, + "password": false, + "name": "verbose", + "advanced": false, + "dynamic": false, + "info": "", + "type": "bool", + "list": false + }, + "_type": "ChatOpenAI" + }, + "description": "`OpenAI` Chat large language models API.", + "base_classes": [ + "ChatOpenAI", + "BaseLanguageModel", + "BaseChatModel", + "BaseLLM" + ], + "display_name": "ChatOpenAI", + "custom_fields": {}, + "output_types": [], + "documentation": "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai" + }, + "id": "ChatOpenAI-U4mZ2", + "value": null + }, + "selected": false, + "positionAbsolute": { + "x": 1557.7805431884235, + "y": -897.7091381330642 + }, + "dragging": false + } + ], + "edges": [ + { + "source": "VectorStoreInfo-z0sH5", + "sourceHandle": "VectorStoreInfo|VectorStoreInfo-z0sH5|VectorStoreInfo", + "target": "VectorStoreAgent-FOmxY", + "targetHandle": "VectorStoreInfo|vectorstoreinfo|VectorStoreAgent-FOmxY", + "className": "", + "id": "reactflow__edge-VectorStoreInfo-z0sH5VectorStoreInfo|VectorStoreInfo-z0sH5|VectorStoreInfo-VectorStoreAgent-FOmxYVectorStoreInfo|vectorstoreinfo|VectorStoreAgent-FOmxY", + "selected": false, + "style": { + "stroke": "#555" + }, + "animated": false + }, + { + "source": "Chroma-UK4a8", + "sourceHandle": "Chroma|Chroma-UK4a8|VectorStore|Chroma|BaseRetriever|VectorStoreRetriever", + "target": "VectorStoreInfo-z0sH5", + "targetHandle": "VectorStore|vectorstore|VectorStoreInfo-z0sH5", + "style": { + "stroke": "#555" + }, + "className": "", + "animated": false, + "id": "reactflow__edge-Chroma-UK4a8Chroma|Chroma-UK4a8|VectorStore|Chroma|BaseRetriever|VectorStoreRetriever-VectorStoreInfo-z0sH5VectorStore|vectorstore|VectorStoreInfo-z0sH5", + "selected": false + }, + { + "source": "WebBaseLoader-aUAEE", + "sourceHandle": "WebBaseLoader|WebBaseLoader-aUAEE|Document", + "target": "RecursiveCharacterTextSplitter-AUWrU", + "targetHandle": "Document|documents|RecursiveCharacterTextSplitter-AUWrU", + "style": { + "stroke": "#555" + }, + "className": "", + "animated": false, + "id": "reactflow__edge-WebBaseLoader-aUAEEWebBaseLoader|WebBaseLoader-aUAEE|Document-RecursiveCharacterTextSplitter-AUWrUDocument|documents|RecursiveCharacterTextSplitter-AUWrU", + "selected": false + }, + { + "source": "RecursiveCharacterTextSplitter-AUWrU", + "sourceHandle": "RecursiveCharacterTextSplitter|RecursiveCharacterTextSplitter-AUWrU|Document", + "target": "Chroma-UK4a8", + "targetHandle": "Document|documents|Chroma-UK4a8", + "style": { + "stroke": "#555" + }, + "className": "", + "animated": false, + "id": "reactflow__edge-RecursiveCharacterTextSplitter-AUWrURecursiveCharacterTextSplitter|RecursiveCharacterTextSplitter-AUWrU|Document-Chroma-UK4a8Document|documents|Chroma-UK4a8", + "selected": false + }, + { + "source": "ChatOpenAI-U4mZ2", + "sourceHandle": "ChatOpenAI|ChatOpenAI-U4mZ2|ChatOpenAI|BaseLanguageModel|BaseChatModel|BaseLLM", + "target": "VectorStoreAgent-FOmxY", + "targetHandle": "BaseLanguageModel|llm|VectorStoreAgent-FOmxY", + "style": { + "stroke": "#555" + }, + "className": "", + "animated": false, + "id": "reactflow__edge-ChatOpenAI-U4mZ2ChatOpenAI|ChatOpenAI-U4mZ2|ChatOpenAI|BaseLanguageModel|BaseChatModel|BaseLLM-VectorStoreAgent-FOmxYBaseLanguageModel|llm|VectorStoreAgent-FOmxY", + "selected": false + }, + { + "source": "OpenAIEmbeddings-lge2J", + "sourceHandle": "OpenAIEmbeddings|OpenAIEmbeddings-lge2J|OpenAIEmbeddings|Embeddings", + "target": "Chroma-UK4a8", + "targetHandle": "Embeddings|embedding|Chroma-UK4a8", + "style": { + "stroke": "#555" + }, + "className": "", + "animated": false, + "id": "reactflow__edge-OpenAIEmbeddings-lge2JOpenAIEmbeddings|OpenAIEmbeddings-lge2J|OpenAIEmbeddings|Embeddings-Chroma-UK4a8Embeddings|embedding|Chroma-UK4a8" + } + ], + "viewport": { + "x": 23.25459650899495, + "y": 727.4174391025257, + "zoom": 0.3802259585247222 + } + }, + "id": "cc9d45a0-a071-4435-9e90-32ccbd1a972b", + "user_id": "c65bfea3-3eea-4e71-8fc4-106238eb0583" +} \ No newline at end of file diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index cb3755db0..8f16e11b5 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -451,6 +451,23 @@ def test_basic_chat_with_two_session_ids_and_names(client, added_flow, created_a assert name in response_json["result"]["text"] +def test_vector_store_in_process(client, added_vector_store, created_api_key): + # Run the /api/v1/process/{flow_id} endpoint + headers = {"x-api-key": created_api_key.api_key} + post_data = {"inputs": {"input": "What is Langflow?"}} + response = client.post( + f"api/v1/process/{added_vector_store.get('id')}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + # Check the response + assert "Langflow" in response.json()["result"]["output"] + # session_id should be returned + assert "session_id" in response.json() + assert response.json()["session_id"] is not None + + # Test function without loop @pytest.mark.async_test def test_async_task_processing(client, added_flow, created_api_key): @@ -477,3 +494,37 @@ def test_async_task_processing(client, added_flow, created_api_key): assert "result" in task_status_json, task_status_json assert "text" in task_status_json["result"], task_status_json["result"] assert "Gabriel" in task_status_json["result"]["text"], task_status_json["result"] + + +# Test function without loop +@pytest.mark.async_test +def test_async_task_processing_vector_store( + client, added_vector_store, created_api_key +): + headers = {"x-api-key": created_api_key.api_key} + post_data = {"inputs": {"input": "What is Langflow?"}} + + # Run the /api/v1/process/{flow_id} endpoint with sync=False + response = client.post( + f"api/v1/process/{added_vector_store.get('id')}", + headers=headers, + json={**post_data, "sync": False}, + ) + assert response.status_code == 200, response.json() + assert "result" in response.json() + assert "FAILURE" not in response.json()["result"] + + # Extract the task ID from the response + task_id = response.json().get("id") + assert task_id is not None + + # Polling the task status using the helper function + task_status_json = poll_task_status(client, headers, task_id, max_attempts=40) + assert task_status_json is not None, "Task did not complete in time" + + # Validate that the task completed successfully and the result is as expected + assert "result" in task_status_json, task_status_json + assert "output" in task_status_json["result"], task_status_json["result"] + assert "Langflow is" in task_status_json["result"]["output"], task_status_json[ + "result" + ] From 8a545d4dcceb90161de8652e22a1120b9ba92973 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:35:10 -0300 Subject: [PATCH 142/213] =?UTF-8?q?=F0=9F=90=9B=20fix(frontend):=20update?= =?UTF-8?q?=20format=20script=20in=20package.json=20to=20only=20format=20f?= =?UTF-8?q?iles=20in=20the=20root=20directory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/package.json b/src/frontend/package.json index 8d7d25d88..720af56fe 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -72,7 +72,7 @@ "start": "vite", "build": "vite build", "serve": "vite preview", - "format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"", + "format": "npx prettier --write \"./*.{js,jsx,ts,tsx,json,md}\"", "type-check": "tsc --noEmit --pretty --project tsconfig.json && vite" }, "eslintConfig": { From d5623cb7136777933f6a38bbc6a920b704286494 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:35:25 -0300 Subject: [PATCH 143/213] =?UTF-8?q?=F0=9F=90=9B=20fix(manager.py):=20fix?= =?UTF-8?q?=20typo=20in=20comment,=20change=20"ached"=20to=20"cached"=20fo?= =?UTF-8?q?r=20better=20readability=20=E2=9C=A8=20feat(manager.py):=20add?= =?UTF-8?q?=20error=20handling=20for=20non-picklable=20values=20in=20Redis?= =?UTF-8?q?Cache's=20set=20method=20to=20prevent=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/manager.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index 843eb182e..fc4a590fb 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -119,7 +119,8 @@ class InMemoryCache(BaseCacheService, Service): def get_or_set(self, key, value): """ - Retrieve an item from the cache. If the item does not exist, set it with the provided value. + Retrieve an item from the cache. If the item does not exist, + set it with the provided value. Args: key: The key of the item. @@ -207,13 +208,15 @@ class RedisCache(BaseCacheService, Service): host (str, optional): Redis host. port (int, optional): Redis port. db (int, optional): Redis DB. - expiration_time (int, optional): Time in seconds after which a cached item expires. Default is 1 hour. + expiration_time (int, optional): Time in seconds after which a + ached item expires. Default is 1 hour. """ try: import redis except ImportError as exc: raise ImportError( - "RedisCache requires the redis-py package. Please install Langflow with the deploy extra: pip install langflow[deploy]" + "RedisCache requires the redis-py package." + " Please install Langflow with the deploy extra: pip install langflow[deploy]" ) from exc self._client = redis.StrictRedis(host=host, port=port, db=db) @@ -253,7 +256,12 @@ class RedisCache(BaseCacheService, Service): key: The key of the item. value: The value to cache. """ - self._client.setex(key, self.expiration_time, pickle.dumps(value)) + try: + self._client.setex(key, self.expiration_time, pickle.dumps(value)) + except TypeError as exc: + raise TypeError( + "RedisCache only accepts values that can be pickled. " + ) from exc def upsert(self, key, value): """ From 9c6cfc07ab2914419137c793b6badf486b0c8726 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:35:40 -0300 Subject: [PATCH 144/213] =?UTF-8?q?=F0=9F=90=9B=20fix(process.py):=20set?= =?UTF-8?q?=20langchain=5Fobject.return=5Fintermediate=5Fsteps=20to=20Fals?= =?UTF-8?q?e=20to=20improve=20performance=20and=20reduce=20memory=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 0fefa8deb..e348743ec 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -67,7 +67,7 @@ def get_result_and_thought(langchain_object: Any, inputs: dict): langchain_object.verbose = True if hasattr(langchain_object, "return_intermediate_steps"): - langchain_object.return_intermediate_steps = True + langchain_object.return_intermediate_steps = False fix_memory_inputs(langchain_object) From c7fc8444ea277096563c0547e024fe74dba264a5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:35:59 -0300 Subject: [PATCH 145/213] =?UTF-8?q?=F0=9F=90=9B=20fix(endpoints.py):=20han?= =?UTF-8?q?dle=20ImportError=20when=20importing=20langflow.worker.process?= =?UTF-8?q?=5Fgraph=5Fcached=5Ftask=20to=20prevent=20crash=20when=20Celery?= =?UTF-8?q?=20is=20not=20installed=20=F0=9F=94=A5=20chore(endpoints.py):?= =?UTF-8?q?=20remove=20unused=20import=20of=20langflow.worker.process=5Fgr?= =?UTF-8?q?aph=5Fcached=5Ftask=20=F0=9F=94=80=20chore(endpoints.py):=20ref?= =?UTF-8?q?actor=20process=5Fflow=20function=20to=20handle=20different=20r?= =?UTF-8?q?esult=20types=20from=20task=5Fservice.launch=5Ftask?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index ec107092b..57b0c6958 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -30,7 +30,15 @@ from langflow.interface.types import ( ) from langflow.services.utils import get_session -from langflow.worker import process_graph_cached_task + +try: + from langflow.worker import process_graph_cached_task +except ImportError: + + def process_graph_cached_task(*args, **kwargs): + raise NotImplementedError("Celery is not installed") + + from sqlmodel import Session @@ -143,7 +151,10 @@ async def process_flow( clear_cache, session_id, ) - task_result = result.result + if isinstance(result, dict) and "result" in result: + task_result = result["result"] + else: + task_result = result.result session_id = result.session_id else: task_id, task = await task_service.launch_task( From 298d9cd1c830744c659db53bb6128c05509c90f3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 17 Sep 2023 19:36:15 -0300 Subject: [PATCH 146/213] =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.yml?= =?UTF-8?q?):=20update=20pytest=20command=20to=20improve=20verbosity=20and?= =?UTF-8?q?=20parallel=20test=20execution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index e8b999dc7..30091b118 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -234,7 +234,7 @@ services: build: context: ../ dockerfile: base.Dockerfile - command: pytest -v --cov=langflow --cov-report=term-missing --cov-report=xml --cov-report=html -m async_test + command: pytest -vv -n auto healthcheck: test: "exit 0" From f01f4a809e8b273b1adbeadfd1e9130f1a536ae0 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:45:02 -0300 Subject: [PATCH 147/213] =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20r?= =?UTF-8?q?ename=20langchain=5Fobject=20variable=20to=20graph=20for=20bett?= =?UTF-8?q?er=20clarity=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20l?= =?UTF-8?q?angchain=5Fobject=20variable=20to=20built=5Fobject=20to=20impro?= =?UTF-8?q?ve=20semantics=20=F0=9F=94=A8=20refactor(worker.py):=20rename?= =?UTF-8?q?=20langchain=5Fobject=20variable=20to=20graph=20for=20better=20?= =?UTF-8?q?clarity=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langch?= =?UTF-8?q?ain=5Fobject=20variable=20to=20built=5Fobject=20to=20improve=20?= =?UTF-8?q?semantics=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20lang?= =?UTF-8?q?chain=5Fobject=20variable=20to=20graph=20for=20better=20clarity?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20built=5Fobject=20to=20improve=20semanti?= =?UTF-8?q?cs=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain?= =?UTF-8?q?=5Fobject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fobje?= =?UTF-8?q?ct=20variable=20to=20built=5Fobject=20to=20improve=20semantics?= =?UTF-8?q?=20=F0=9F=94=A8=20refactor(worker.py):=20rename=20langchain=5Fo?= =?UTF-8?q?bject=20variable=20to=20graph=20for=20better=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/worker.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 018cfa5e7..92f6f1f7c 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -1,6 +1,8 @@ from langflow.core.celery_app import celery_app from typing import Any, Dict, Optional, Tuple from typing import TYPE_CHECKING + +from langflow.interface.run import build_sorted_vertices from celery.exceptions import SoftTimeLimitExceeded # type: ignore from langflow.processing.process import ( Result, @@ -50,11 +52,12 @@ def process_graph_cached_task( session_id=session_id, data_graph=data_graph ) # Load the graph using SessionService - langchain_object, artifacts = session_service.load_session(session_id, data_graph) + graph, artifacts = session_service.load_session(session_id, data_graph) + built_object = graph.build() processed_inputs = process_inputs(inputs, artifacts) - result = generate_result(langchain_object, processed_inputs) + result = generate_result(built_object, processed_inputs) # langchain_object is now updated with the new memory # we need to update the cache with the updated langchain_object - session_service.update_session(session_id, (langchain_object, artifacts)) + session_service.update_session(session_id, (graph, artifacts)) return Result(result=result, session_id=session_id).dict() From a64c7bea7f2cf9e24ae54a02e7cb24fd9efe733b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:45:40 -0300 Subject: [PATCH 148/213] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20chore(pyproject.to?= =?UTF-8?q?ml):=20add=20pytest-sugar=20as=20a=20development=20dependency?= =?UTF-8?q?=20to=20enhance=20test=20reporting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 35 ++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index f292d5352..53e09ac71 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5958,6 +5958,25 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "pytest-sugar" +version = "0.9.7" +description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." +optional = false +python-versions = "*" +files = [ + {file = "pytest-sugar-0.9.7.tar.gz", hash = "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46"}, + {file = "pytest_sugar-0.9.7-py2.py3-none-any.whl", hash = "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062"}, +] + +[package.dependencies] +packaging = ">=21.3" +pytest = ">=6.2.0" +termcolor = ">=2.1.0" + +[package.extras] +dev = ["black", "flake8", "pre-commit"] + [[package]] name = "pytest-xdist" version = "3.3.1" @@ -7226,6 +7245,20 @@ files = [ [package.extras] doc = ["reno", "sphinx", "tornado (>=4.5)"] +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + [[package]] name = "textual" version = "0.37.1" @@ -8629,4 +8662,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.11" -content-hash = "3751ee3efd625b14a7b6cbd6200565a65b5ecd369761aff0537f5f480b899619" +content-hash = "4c507c16376c14157663ca8c03ae1290349f5d90f8dd0732419324564fbe4f93" diff --git a/pyproject.toml b/pyproject.toml index 1b0055752..ef70381b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,6 +111,7 @@ pytest-mock = "^3.11.1" pytest-xdist = "^3.3.1" types-pywin32 = "^306.0.0.4" types-redis = "^4.6.0.5" +pytest-sugar = "^0.9.7" [tool.poetry.extras] From d4f3640e5f33aeaa3bcfa1c480a2b91cbd4a1798 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:46:01 -0300 Subject: [PATCH 149/213] =?UTF-8?q?=F0=9F=90=9B=20fix(Dockerfile):=20copy?= =?UTF-8?q?=20only=20necessary=20file=20to=20avoid=20unnecessary=20image?= =?UTF-8?q?=20rebuilding=20=E2=9C=A8=20feat(Dockerfile):=20add=20support?= =?UTF-8?q?=20for=20copying=20app=20code=20and=20tests=20to=20the=20image?= =?UTF-8?q?=20to=20ensure=20they=20are=20included=20in=20the=20deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.Dockerfile | 12 +++++++++--- deploy/base.Dockerfile | 7 ++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/base.Dockerfile b/base.Dockerfile index aa50fe90f..c76bbbcda 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -83,9 +83,15 @@ WORKDIR $PYSETUP_PATH COPY --from=builder-base $POETRY_HOME $POETRY_HOME COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH -COPY ./src/backend ./src/backend -COPY ./tests ./tests - +# Copy just one file to avoid rebuilding the whole image +COPY ./src/backend/langflow/__init__.py ./src/backend/langflow/__init__.py # quicker install as runtime deps are already installed RUN --mount=type=cache,target=/root/.cache \ poetry install --with=dev --extras deploy + +# copy in our app code +COPY ./src/backend ./src/backend +RUN --mount=type=cache,target=/root/.cache \ + poetry install --with=dev --extras deploy +COPY ./tests ./tests= + diff --git a/deploy/base.Dockerfile b/deploy/base.Dockerfile index 83e875fa6..323663283 100644 --- a/deploy/base.Dockerfile +++ b/deploy/base.Dockerfile @@ -81,7 +81,12 @@ WORKDIR $PYSETUP_PATH COPY --from=builder-base $POETRY_HOME $POETRY_HOME COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH -COPY ./src/backend ./src/backend +# Copy just one file to avoid rebuilding the whole image +COPY ./src/backend/langflow/__init__.py ./src/backend/langflow/__init__.py # quicker install as runtime deps are already installed RUN --mount=type=cache,target=/root/.cache \ poetry install --with=dev --extras deploy + +# copy in our app code +COPY ./src/backend ./src/backend +COPY ./tests ./tests From 8557a728d6e7707f39195431c58e8ee04815ce5f Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:46:18 -0300 Subject: [PATCH 150/213] =?UTF-8?q?=F0=9F=90=9B=20fix(docker-compose.overr?= =?UTF-8?q?ide.yml):=20expose=20Redis=20port=206379=20for=20the=20queue=20?= =?UTF-8?q?service=20=E2=9C=A8=20feat(docker-compose.override.yml):=20add?= =?UTF-8?q?=20configuration=20for=20the=20celeryworker=20service=20to=20en?= =?UTF-8?q?able=20Traefik=20routing=20and=20expose=20port=207860=20for=20A?= =?UTF-8?q?PI=20endpoints=20=F0=9F=90=9B=20fix(docker-compose.yml):=20remo?= =?UTF-8?q?ve=20parallel=20test=20execution=20flag=20from=20the=20command?= =?UTF-8?q?=20for=20the=20test=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.override.yml | 11 +++++++++++ deploy/docker-compose.yml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/deploy/docker-compose.override.yml b/deploy/docker-compose.override.yml index d6984c959..54fa69e9a 100644 --- a/deploy/docker-compose.override.yml +++ b/deploy/docker-compose.override.yml @@ -27,6 +27,10 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`) - traefik.http.services.${STACK_NAME?Variable not set}-traefik-public.loadbalancer.server.port=80 + queue: + ports: + - "6379:6379" + pgadmin: ports: - "5050:5050" @@ -49,6 +53,13 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 + celeryworker: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-celeryworker-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-celeryworker.loadbalancer.server.port=7860 + networks: traefik-public: # For local dev, don't expect an external Traefik network diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 30091b118..328fcfef4 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -234,7 +234,7 @@ services: build: context: ../ dockerfile: base.Dockerfile - command: pytest -vv -n auto + command: pytest -vv healthcheck: test: "exit 0" From 358f5c9019640b99a8b33cba6ecb9f06017cc771 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:46:36 -0300 Subject: [PATCH 151/213] =?UTF-8?q?=F0=9F=90=9B=20fix(endpoints.py):=20fix?= =?UTF-8?q?=20indentation=20issue=20in=20process=5Fflow=20function=20and?= =?UTF-8?q?=20assign=20session=5Fid=20correctly=20when=20result=20is=20a?= =?UTF-8?q?=20dictionary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 57b0c6958..e4e22bf7d 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -153,9 +153,11 @@ async def process_flow( ) if isinstance(result, dict) and "result" in result: task_result = result["result"] + session_id = result["session_id"] else: task_result = result.result - session_id = result.session_id + + session_id = result.session_id else: task_id, task = await task_service.launch_task( process_graph_cached_task From 3412e8a92cdde3ff7239f7f2ff83863f08f03cf2 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:47:35 -0300 Subject: [PATCH 152/213] =?UTF-8?q?=F0=9F=94=A7=20chore(celery=5Fapp.py):?= =?UTF-8?q?=20modify=20make=5Fcelery=20function=20signature=20to=20accept?= =?UTF-8?q?=20a=20config=20parameter=20=F0=9F=94=A7=20chore(celery=5Fapp.p?= =?UTF-8?q?y):=20update=20celery=5Fapp.config=5Ffrom=5Fobject=20argument?= =?UTF-8?q?=20to=20use=20the=20config=20parameter=20passed=20to=20make=5Fc?= =?UTF-8?q?elery=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/core/celery_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/core/celery_app.py b/src/backend/langflow/core/celery_app.py index 699fc3e14..85e2bc2d2 100644 --- a/src/backend/langflow/core/celery_app.py +++ b/src/backend/langflow/core/celery_app.py @@ -1,11 +1,11 @@ from celery import Celery # type: ignore -def make_celery(app_name: str): +def make_celery(app_name: str, config: str): celery_app = Celery(app_name) - celery_app.config_from_object("langflow.core.celeryconfig") + celery_app.config_from_object(config) celery_app.conf.task_routes = {"langflow.worker.tasks.*": {"queue": "langflow"}} return celery_app -celery_app = make_celery("langflow") +celery_app = make_celery("langflow", "langflow.core.celeryconfig") From df192986379bf7a99c45b81c366e8ba9180416c7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:48:13 -0300 Subject: [PATCH 153/213] =?UTF-8?q?=F0=9F=90=9B=20fix(cache/manager.py):?= =?UTF-8?q?=20unpickle=20cached=20value=20before=20returning=20it=20to=20f?= =?UTF-8?q?ix=20deserialization=20issue=20=F0=9F=90=9B=20fix(cache/manager?= =?UTF-8?q?.py):=20pickle=20value=20before=20caching=20it=20to=20mimic=20R?= =?UTF-8?q?edis=20behavior=20=F0=9F=90=9B=20fix(cache/manager.py):=20raise?= =?UTF-8?q?=20ValueError=20if=20RedisCache=20fails=20to=20set=20the=20valu?= =?UTF-8?q?e=20=F0=9F=90=9B=20fix(session/manager.py):=20generate=20key=20?= =?UTF-8?q?if=20it=20is=20None=20before=20checking=20cache=20=E2=9C=A8=20f?= =?UTF-8?q?eat(session/manager.py):=20add=20logging=20import=20to=20enable?= =?UTF-8?q?=20logging=20in=20the=20session=20manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/manager.py | 12 +++++++++--- src/backend/langflow/services/session/manager.py | 6 +++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index fc4a590fb..b73908e51 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -72,7 +72,8 @@ class InMemoryCache(BaseCacheService, Service): ): # Move the key to the end to make it recently used self._cache.move_to_end(key) - return item["value"] + unpickled = pickle.loads(item["value"]) + return unpickled else: self.delete(key) return None @@ -94,7 +95,9 @@ class InMemoryCache(BaseCacheService, Service): elif self.max_size and len(self._cache) >= self.max_size: # Remove least recently used item self._cache.popitem(last=False) - self._cache[key] = {"value": value, "time": time.time()} + # pickle locally to mimic Redis + pickled = pickle.dumps(value) + self._cache[key] = {"value": pickled, "time": time.time()} def upsert(self, key, value): """ @@ -257,7 +260,10 @@ class RedisCache(BaseCacheService, Service): value: The value to cache. """ try: - self._client.setex(key, self.expiration_time, pickle.dumps(value)) + if pickled := pickle.dumps(value): + result = self._client.setex(key, self.expiration_time, pickled) + if not result: + raise ValueError("RedisCache could not set the value.") except TypeError as exc: raise TypeError( "RedisCache only accepts values that can be pickled. " diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index 44108b14f..e0ddeb0c2 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING from langflow.interface.run import build_sorted_vertices from langflow.services.base import Service from langflow.services.cache.utils import compute_dict_hash - +from loguru import logger from langflow.services.session.utils import session_id_generator if TYPE_CHECKING: @@ -20,8 +20,12 @@ class SessionService(Service): if key in self.cache_service: return self.cache_service.get(key) + if key is None: + key = self.generate_key(session_id=None, data_graph=data_graph) + # If not cached, build the graph and cache it graph, artifacts = build_sorted_vertices(data_graph) + self.cache_service.set(key, (graph, artifacts)) return graph, artifacts From 2b10cfe96d9ff24ccdc7f9248f78e286a0299498 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:54:30 -0300 Subject: [PATCH 154/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20add=20mi?= =?UTF-8?q?ssing=20import=20statement=20for=20is=5Fbasic=5Ftype=20function?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(base.py):=20add=20missing=20import=20stat?= =?UTF-8?q?ement=20for=20logger=20=F0=9F=90=9B=20fix(base.py):=20handle=20?= =?UTF-8?q?AttributeError=20when=20comparing=20Vertex=20objects=20for=20eq?= =?UTF-8?q?uality=20=F0=9F=90=9B=20fix(base.py):=20handle=20exception=20an?= =?UTF-8?q?d=20log=20it=20when=20building=20node=20fails=20=F0=9F=90=9B=20?= =?UTF-8?q?fix(base.py):=20handle=20exception=20and=20log=20it=20when=20pi?= =?UTF-8?q?ckling=20built=20object=20fails=20=F0=9F=90=9B=20fix(base.py):?= =?UTF-8?q?=20reset=20params=20and=20rebuild=20built=20object=20when=20pic?= =?UTF-8?q?kling=20built=20object=20fails=20=F0=9F=90=9B=20fix(base.py):?= =?UTF-8?q?=20handle=20exception=20and=20log=20it=20when=20pickling=20buil?= =?UTF-8?q?t=20object=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle=20ex?= =?UTF-8?q?ception=20and=20log=20it=20when=20pickling=20built=20object=20f?= =?UTF-8?q?ails=20=F0=9F=90=9B=20fix(base.py):=20handle=20exception=20and?= =?UTF-8?q?=20log=20it=20when=20pickling=20built=20object=20fails=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20handle=20exception=20and=20log?= =?UTF-8?q?=20it=20when=20pickling=20built=20object=20fails=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(base.py):=20handle=20exception=20and=20log=20it=20when?= =?UTF-8?q?=20pickling=20built=20object=20fails=20=F0=9F=90=9B=20fix(base.?= =?UTF-8?q?py):=20handle=20exception=20and=20log=20it=20when=20pickling=20?= =?UTF-8?q?built=20object=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle?= =?UTF-8?q?=20exception=20and=20log=20it=20when=20pickling=20built=20objec?= =?UTF-8?q?t=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle=20exception?= =?UTF-8?q?=20and=20log=20it=20when=20pickling=20built=20object=20fails=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20handle=20exception=20and=20log?= =?UTF-8?q?=20it=20when=20pickling=20built=20object=20fails=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(base.py):=20handle=20exception=20and=20log=20it=20when?= =?UTF-8?q?=20pickling=20built=20object=20fails=20=F0=9F=90=9B=20fix(base.?= =?UTF-8?q?py):=20handle=20exception=20and=20log=20it=20when=20pickling=20?= =?UTF-8?q?built=20object=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle?= =?UTF-8?q?=20exception=20and=20log=20it=20when=20pickling=20built=20objec?= =?UTF-8?q?t=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle=20exception?= =?UTF-8?q?=20and=20log=20it=20when=20pickling=20built=20object=20fails=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20handle=20exception=20and=20log?= =?UTF-8?q?=20it=20when=20pickling=20built=20object=20fails=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(base.py):=20handle=20exception=20and=20log=20it=20when?= =?UTF-8?q?=20pickling=20built=20object=20fails=20=F0=9F=90=9B=20fix(base.?= =?UTF-8?q?py):=20handle=20exception=20and=20log=20it=20when=20pickling=20?= =?UTF-8?q?built=20object=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle?= =?UTF-8?q?=20exception=20and=20log=20it=20when=20pickling=20built=20objec?= =?UTF-8?q?t=20fails=20=F0=9F=90=9B=20fix(base.py):=20handle=20exception?= =?UTF-8?q?=20and=20log=20it=20when=20pickling=20built=20object=20fails=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20handle=20exception=20and=20log?= =?UTF-8?q?=20it=20when=20pickling=20built=20object=20fails=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(base.py):=20handle=20exception=20and=20log=20it=20when?= =?UTF-8?q?=20pickling=20built=20object=20fails=20=F0=9F=90=9B=20fix(base.?= =?UTF-8?q?py):=20handle=20exception=20and=20log=20it=20when=20pickling=20?= =?UTF-8?q?built=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/base.py | 72 ++++++++++++++++++++++- src/backend/langflow/utils/constants.py | 1 + 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index d8de30ca9..4c6262554 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -1,5 +1,7 @@ import ast +import pickle from langflow.graph.utils import UnbuiltObject +from langflow.graph.vertex.utils import is_basic_type from langflow.interface.initialize import loading from langflow.interface.listing import lazy_load_dict from langflow.utils.constants import DIRECT_TYPES @@ -19,7 +21,11 @@ if TYPE_CHECKING: class Vertex: def __init__( - self, data: Dict, base_type: Optional[str] = None, is_task: bool = False + self, + data: Dict, + base_type: Optional[str] = None, + is_task: bool = False, + params: Optional[Dict] = None, ) -> None: self.id: str = data["id"] self._data = data @@ -31,6 +37,57 @@ class Vertex: self.artifacts: Dict[str, Any] = {} self.task_id: Optional[str] = None self.is_task = is_task + self.params = params + + def reset_params(self): + for edge in self.edges: + if edge.source != self: + target_param = edge.target_param + if target_param in ["document", "texts"]: + # this means they got data and have already ingested it + # so we continue after removing the param + self.params.pop(target_param, None) + continue + + if target_param in self.params and not is_basic_type( + self.params[target_param] + ): + # edge.source.params = {} + edge.source._build_params() + edge.source._built_object = UnbuiltObject() + edge.source._built = False + + self.params[target_param] = edge.source + + def __getstate__(self): + state_dict = self.__dict__.copy() + try: + # try pickling the built object + # if it fails, then we need to delete it + # and build it again + pickle.dumps(state_dict["_built_object"]) + except Exception: + self.reset_params() + del state_dict["_built_object"] + del state_dict["_built"] + return state_dict + + def __setstate__(self, state): + self._data = state["_data"] + self.params = state["params"] + self.base_type = state["base_type"] + self.is_task = state["is_task"] + self.edges = state["edges"] + self.id = state["id"] + self._parse_data() + if "_built_object" in state: + self._built_object = state["_built_object"] + self._built = state["_built"] + else: + self._built_object = UnbuiltObject() + self._built = False + self.artifacts: Dict[str, Any] = {} + self.task_id: Optional[str] = None def _parse_data(self) -> None: self.data = self._data["data"] @@ -101,9 +158,11 @@ class Vertex: for key, value in self.data["node"]["template"].items() if isinstance(value, dict) } - params = {} + params = self.params.copy() if self.params else {} for edge in self.edges: + if not hasattr(edge, "target_param"): + continue param_key = edge.target_param if param_key in template_dict: if template_dict[param_key]["list"]: @@ -114,6 +173,8 @@ class Vertex: params[param_key] = edge.source for key, value in template_dict.items(): + if key in params: + continue # Skip _type and any value that has show == False and is not code # If we don't want to show code but we want to use it if key == "_type" or (not value.get("show") and key != "code"): @@ -143,6 +204,7 @@ class Vertex: else: params.pop(key, None) # Add _type to params + self._raw_params = params self.params = params def _build(self, user_id=None): @@ -264,6 +326,7 @@ class Vertex: ) self._update_built_object_and_artifacts(result) except Exception as exc: + logger.exception(exc) raise ValueError( f"Error building node {self.vertex_type}: {str(exc)}" ) from exc @@ -304,7 +367,10 @@ class Vertex: return f"Vertex(id={self.id}, data={self.data})" def __eq__(self, __o: object) -> bool: - return self.id == __o.id if isinstance(__o, Vertex) else False + try: + return self.id == __o.id if isinstance(__o, Vertex) else False + except AttributeError: + return False def __hash__(self) -> int: return id(self) diff --git a/src/backend/langflow/utils/constants.py b/src/backend/langflow/utils/constants.py index e473d855b..adf4cf63c 100644 --- a/src/backend/langflow/utils/constants.py +++ b/src/backend/langflow/utils/constants.py @@ -49,3 +49,4 @@ def python_function(text: str) -> str: """ DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"] +PYTHON_BASIC_TYPES = [str, bool, int, float, tuple, list, dict, set] From bea1328a3e3ccf87a3cb3cf86afd9973549a69a7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:58:27 -0300 Subject: [PATCH 155/213] =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20add=20?= =?UTF-8?q?=5F=5Fsetstate=5F=5F=20method=20to=20Edge=20class=20to=20proper?= =?UTF-8?q?ly=20set=20state=20when=20unpickling=20=F0=9F=90=9B=20fix(base.?= =?UTF-8?q?py):=20add=20reset=20method=20to=20Edge=20class=20to=20reset=20?= =?UTF-8?q?source=20and=20target=20params=20when=20needed=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(base.py):=20add=20=5F=5Fsetstate=5F=5F=20method=20to=20G?= =?UTF-8?q?raph=20class=20to=20properly=20set=20state=20when=20unpickling?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(base.py):=20add=20=5F=5Feq=5F=5F=20method?= =?UTF-8?q?=20to=20Graph=20class=20to=20compare=20graphs=20based=20on=20th?= =?UTF-8?q?eir=20string=20representation=20=F0=9F=90=9B=20fix(types.py):?= =?UTF-8?q?=20add=20=5F=5Fgetstate=5F=5F=20and=20=5F=5Fsetstate=5F=5F=20me?= =?UTF-8?q?thods=20to=20AgentVertex=20class=20to=20properly=20set=20and=20?= =?UTF-8?q?get=20state=20when=20pickling=20and=20unpickling=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(types.py):=20add=20=5F=5Fgetstate=5F=5F=20and=20=5F=5Fse?= =?UTF-8?q?tstate=5F=5F=20methods=20to=20ToolVertex=20class=20to=20properl?= =?UTF-8?q?y=20set=20and=20get=20state=20when=20pickling=20and=20unpicklin?= =?UTF-8?q?g=20=F0=9F=90=9B=20fix(types.py):=20add=20=5F=5Fgetstate=5F=5F?= =?UTF-8?q?=20and=20=5F=5Fsetstate=5F=5F=20methods=20to=20LLMVertex=20clas?= =?UTF-8?q?s=20to=20properly=20set=20and=20get=20state=20when=20pickling?= =?UTF-8?q?=20and=20unpickling=20=F0=9F=90=9B=20fix(types.py):=20add=20=5F?= =?UTF-8?q?=5Fgetstate=5F=5F=20and=20=5F=5Fsetstate=5F=5F=20methods=20to?= =?UTF-8?q?=20ToolkitVertex=20class=20to=20properly=20set=20and=20get=20st?= =?UTF-8?q?ate=20when=20pickling=20and=20unpickling=20=F0=9F=90=9B=20fix(t?= =?UTF-8?q?ypes.py):=20add=20=5F=5Fgetstate=5F=5F=20and=20=5F=5Fsetstate?= =?UTF-8?q?=5F=5F=20methods=20to=20FileToolVertex=20class=20to=20properly?= =?UTF-8?q?=20set=20and=20get=20state=20when=20pickling=20and=20unpickling?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(types.py):=20add=20=5F=5Fgetstate=5F=5F?= =?UTF-8?q?=20and=20=5F=5Fsetstate=5F=5F=20methods=20to=20DocumentLoaderVe?= =?UTF-8?q?rtex=20class=20to=20properly=20set=20and=20get=20state=20when?= =?UTF-8?q?=20pickling=20and=20unpickling=20=F0=9F=90=9B=20fix(types.py):?= =?UTF-8?q?=20add=20=5F=5Fgetstate=5F=5F=20and=20=5F=5Fsetstate=5F=5F=20me?= =?UTF-8?q?thods=20to=20EmbeddingVertex=20class=20to=20properly=20set=20an?= =?UTF-8?q?d=20get=20state=20when=20pickling=20and=20unpickling=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(types.py):=20add=20=5F=5Fgetstate=5F=5F=20an?= =?UTF-8?q?d=20=5F=5Fsetstate=5F=5F=20methods=20to=20VectorStoreVertex=20c?= =?UTF-8?q?lass=20to=20properly=20set=20and=20get=20state=20when=20picklin?= =?UTF-8?q?g=20and=20unpickling=20=F0=9F=90=9B=20fix(types.py):=20add=20?= =?UTF-8?q?=5F=5Fgetstate=5F=5F=20and=20=5F=5Fsetstate=5F=5F=20methods=20t?= =?UTF-8?q?o=20TextSplitterVertex=20class=20to=20properly=20set=20and=20ge?= =?UTF-8?q?t=20state=20when=20pickling=20and=20unpickling=20=E2=9C=A8=20fe?= =?UTF-8?q?at(types.py):=20add=20reset=20method=20to=20AgentVertex=20class?= =?UTF-8?q?=20to=20reset=20source=20and=20target=20params=20when=20needed?= =?UTF-8?q?=20=E2=9C=A8=20feat(types.py):=20add=20reset=20method=20to=20To?= =?UTF-8?q?olVertex=20class=20to=20reset=20source=20and=20target=20params?= =?UTF-8?q?=20when=20needed=20=E2=9C=A8=20feat(types.py):=20add=20reset=20?= =?UTF-8?q?method=20to=20LLMVertex=20class=20to=20reset=20source=20and=20t?= =?UTF-8?q?arget=20params=20when=20needed=20=E2=9C=A8=20feat(types.py):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/edge/base.py | 11 +++ src/backend/langflow/graph/graph/base.py | 11 +++ src/backend/langflow/graph/vertex/types.py | 93 +++++++++++++++++----- 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/src/backend/langflow/graph/edge/base.py b/src/backend/langflow/graph/edge/base.py index 2df20cbde..2c60b0288 100644 --- a/src/backend/langflow/graph/edge/base.py +++ b/src/backend/langflow/graph/edge/base.py @@ -17,6 +17,17 @@ class Edge: self.validate_edge() + def __setstate__(self, state): + self.source = state["source"] + self.target = state["target"] + self.target_param = state["target_param"] + self.source_handle = state["source_handle"] + self.target_handle = state["target_handle"] + + def reset(self) -> None: + self.source._build_params() + self.target._build_params() + def validate_edge(self) -> None: # Validate that the outputs of the source node are valid inputs # for the target node diff --git a/src/backend/langflow/graph/graph/base.py b/src/backend/langflow/graph/graph/base.py index a8b9ee592..227b04bf9 100644 --- a/src/backend/langflow/graph/graph/base.py +++ b/src/backend/langflow/graph/graph/base.py @@ -26,6 +26,12 @@ class Graph: self._edges = edges self._build_graph() + def __setstate__(self, state): + self.__dict__.update(state) + for edge in self.edges: + edge.reset() + edge.validate_edge() + @classmethod def from_payload(cls, payload: Dict) -> "Graph": """ @@ -48,6 +54,11 @@ class Graph: f"Invalid payload. Expected keys 'nodes' and 'edges'. Found {list(payload.keys())}" ) from exc + def __eq__(self, other: object) -> bool: + if not isinstance(other, Graph): + return False + return self.__repr__() == other.__repr__() + def _build_graph(self) -> None: """Builds the graph from the nodes and edges.""" self.nodes = self._build_vertices() diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index fecf75728..a64324285 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -4,17 +4,31 @@ from typing import Any, Dict, List, Optional, Union from langflow.graph.vertex.base import Vertex from langflow.graph.utils import flatten_list from langflow.interface.utils import extract_input_variables_from_prompt +from zmq import has class AgentVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="agents") + def __init__(self, data: Dict, params: Optional[Dict] = None): + super().__init__(data, base_type="agents", params=params) self.tools: List[Union[ToolkitVertex, ToolVertex]] = [] self.chains: List[ChainVertex] = [] + def __getstate__(self): + state = super().__getstate__() + state["tools"] = self.tools + state["chains"] = self.chains + return state + + def __setstate__(self, state): + self.tools = state["tools"] + self.chains = state["chains"] + super().__setstate__(state) + def _set_tools_and_chains(self) -> None: for edge in self.edges: + if not hasattr(edge, "source"): + continue source_node = edge.source if isinstance(source_node, (ToolVertex, ToolkitVertex)): self.tools.append(source_node) @@ -38,16 +52,16 @@ class AgentVertex(Vertex): class ToolVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="tools") + def __init__(self, data: Dict, params: Optional[Dict] = None): + super().__init__(data, base_type="tools", params=params) class LLMVertex(Vertex): built_node_type = None class_built_object = None - def __init__(self, data: Dict): - super().__init__(data, base_type="llms") + def __init__(self, data: Dict, params: Optional[Dict] = None): + super().__init__(data, base_type="llms", params=params) def build(self, force: bool = False, user_id=None, *args, **kwargs) -> Any: # LLM is different because some models might take up too much memory @@ -64,13 +78,13 @@ class LLMVertex(Vertex): class ToolkitVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="toolkits") + def __init__(self, data: Dict, params=None): + super().__init__(data, base_type="toolkits", params=params) class FileToolVertex(ToolVertex): - def __init__(self, data: Dict): - super().__init__(data) + def __init__(self, data: Dict, params=None): + super().__init__(data, params=params) class WrapperVertex(Vertex): @@ -86,17 +100,19 @@ class WrapperVertex(Vertex): class DocumentLoaderVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="documentloaders") + def __init__(self, data: Dict, params: Optional[Dict] = None): + super().__init__(data, base_type="documentloaders", params=params) def _built_object_repr(self): # This built_object is a list of documents. Maybe we should # show how many documents are in the list? if self._built_object: - avg_length = sum(len(doc.page_content) for doc in self._built_object) / len( - self._built_object - ) + avg_length = sum( + len(doc.page_content) + for doc in self._built_object + if hasattr(doc, "page_content") + ) / len(self._built_object) return f"""{self.vertex_type}({len(self._built_object)} documents) \nAvg. Document Length (characters): {int(avg_length)} Documents: {self._built_object[:3]}...""" @@ -104,13 +120,50 @@ class DocumentLoaderVertex(Vertex): class EmbeddingVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="embeddings") + def __init__(self, data: Dict, params: Optional[Dict] = None): + super().__init__(data, base_type="embeddings", params=params) class VectorStoreVertex(Vertex): - def __init__(self, data: Dict): + def __init__(self, data: Dict, params=None): super().__init__(data, base_type="vectorstores") + if params: + self.params = params + + # VectorStores may contain databse connections + # so we need to define the __reduce__ method and the __setstate__ method + # to avoid pickling errors + def clean_edges_for_pickling(self): + # for each edge that has self as source + # we need to clear the _built_object of the target + # so that we don't try to pickle a database connection + for edge in self.edges: + if edge.source == self: + edge.target._built_object = None + edge.target._built = False + edge.target.params[edge.target_param] = self + + def remove_docs_and_texts_from_params(self): + # remove documents and texts from params + # so that we don't try to pickle a database connection + self.params.pop("documents", None) + self.params.pop("texts", None) + + def __getstate__(self): + # We want to save the params attribute + # and if "documents" or "texts" are in the params + # we want to remove them because they have already + # been processed. + params = self.params.copy() + params.pop("documents", None) + params.pop("texts", None) + self.clean_edges_for_pickling() + + return super().__getstate__() + + def __setstate__(self, state): + super().__setstate__(state) + self.remove_docs_and_texts_from_params() class MemoryVertex(Vertex): @@ -124,8 +177,8 @@ class RetrieverVertex(Vertex): class TextSplitterVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="textsplitters") + def __init__(self, data: Dict, params: Optional[Dict] = None): + super().__init__(data, base_type="textsplitters", params=params) def _built_object_repr(self): # This built_object is a list of documents. Maybe we should From 04fe2f60546bf33b20c231978da0dcb99cefc9ab Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 10:59:53 -0300 Subject: [PATCH 156/213] =?UTF-8?q?=F0=9F=93=9D=20chore(utils.py):=20add?= =?UTF-8?q?=20utility=20function=20to=20check=20if=20an=20object=20is=20a?= =?UTF-8?q?=20basic=20type=20=F0=9F=93=9D=20chore(loading.py):=20refactor?= =?UTF-8?q?=20code=20to=20improve=20readability=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=93=9D=20chore(vector=5Fstore.py):=20refactor=20code=20to?= =?UTF-8?q?=20improve=20readability=20and=20maintainability=20=F0=9F=93=9D?= =?UTF-8?q?=20chore(run.py):=20update=20return=20type=20hint=20for=20build?= =?UTF-8?q?=5Fsorted=5Fvertices=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/utils.py | 5 +++++ .../langflow/interface/initialize/loading.py | 20 ++++++++++++++++++- .../interface/initialize/vector_store.py | 7 ++++++- src/backend/langflow/interface/run.py | 4 ++-- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 src/backend/langflow/graph/vertex/utils.py diff --git a/src/backend/langflow/graph/vertex/utils.py b/src/backend/langflow/graph/vertex/utils.py new file mode 100644 index 000000000..e1d439f4b --- /dev/null +++ b/src/backend/langflow/graph/vertex/utils.py @@ -0,0 +1,5 @@ +from langflow.utils.constants import PYTHON_BASIC_TYPES + + +def is_basic_type(obj): + return type(obj) in PYTHON_BASIC_TYPES diff --git a/src/backend/langflow/interface/initialize/loading.py b/src/backend/langflow/interface/initialize/loading.py index b600fb5b2..82075cd2e 100644 --- a/src/backend/langflow/interface/initialize/loading.py +++ b/src/backend/langflow/interface/initialize/loading.py @@ -1,7 +1,7 @@ import json import orjson from typing import Any, Callable, Dict, Sequence, Type, TYPE_CHECKING - +from langchain.schema import Document from langchain.agents import agent as agent_module from langchain.agents.agent import AgentExecutor from langchain.agents.agent_toolkits.base import BaseToolkit @@ -40,12 +40,23 @@ if TYPE_CHECKING: from langflow import CustomComponent +def build_vertex_in_params(params: Dict) -> Dict: + from langflow.graph.vertex.base import Vertex + + # If any of the values in params is a Vertex, we will build it + return { + key: value.build() if isinstance(value, Vertex) else value + for key, value in params.items() + } + + def instantiate_class( node_type: str, base_type: str, params: Dict, user_id=None ) -> Any: """Instantiate class from module type and key, and params""" params = convert_params_to_sets(params) params = convert_kwargs(params) + if node_type in CUSTOM_NODES: if custom_node := CUSTOM_NODES.get(node_type): if hasattr(custom_node, "initialize"): @@ -289,6 +300,13 @@ def instantiate_embedding(node_type, class_object, params: Dict): def instantiate_vectorstore(class_object: Type[VectorStore], params: Dict): search_kwargs = params.pop("search_kwargs", {}) + # clean up docs or texts to have only documents + if "texts" in params: + params["documents"] = params.pop("texts") + if "documents" in params: + params["documents"] = [ + doc for doc in params["documents"] if isinstance(doc, Document) + ] if initializer := vecstore_initializer.get(class_object.__name__): vecstore = initializer(class_object, params) else: diff --git a/src/backend/langflow/interface/initialize/vector_store.py b/src/backend/langflow/interface/initialize/vector_store.py index 233292626..59e7c0d3b 100644 --- a/src/backend/langflow/interface/initialize/vector_store.py +++ b/src/backend/langflow/interface/initialize/vector_store.py @@ -8,7 +8,7 @@ from langchain.vectorstores import ( SupabaseVectorStore, MongoDBAtlasVectorSearch, ) - +from langchain.schema import Document import os import orjson @@ -201,11 +201,16 @@ def initialize_chroma(class_object: Type[Chroma], params: dict): if "texts" in params: params["documents"] = params.pop("texts") for doc in params["documents"]: + if not isinstance(doc, Document): + # remove any non-Document objects from the list + params["documents"].remove(doc) + continue if doc.metadata is None: doc.metadata = {} for key, value in doc.metadata.items(): if value is None: doc.metadata[key] = "" + chromadb = class_object.from_documents(**params) if persist: chromadb.persist() diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 81c3f6910..8df41d3b3 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -3,7 +3,7 @@ from langflow.graph import Graph from loguru import logger -def build_sorted_vertices(data_graph) -> Tuple[Any, Dict]: +def build_sorted_vertices(data_graph) -> Tuple[Graph, Dict]: """ Build langchain object from data_graph. """ @@ -16,7 +16,7 @@ def build_sorted_vertices(data_graph) -> Tuple[Any, Dict]: vertex.build() if vertex.artifacts: artifacts.update(vertex.artifacts) - return graph.build(), artifacts + return graph, artifacts def build_langchain_object(data_graph): From f8b38ee162c62da027b616adc40d8557dd44f554 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 11:00:57 -0300 Subject: [PATCH 157/213] =?UTF-8?q?=F0=9F=90=9B=20fix(process.py):=20renam?= =?UTF-8?q?e=20langchain=5Fobject=20variable=20to=20graph=20for=20better?= =?UTF-8?q?=20semantics=20=F0=9F=90=9B=20fix(process.py):=20rename=20langc?= =?UTF-8?q?hain=5Fobject=20variable=20to=20built=5Fobject=20in=20generate?= =?UTF-8?q?=5Fresult=20function=20for=20better=20semantics=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(process.py):=20update=20session=20with=20graph=20instead?= =?UTF-8?q?=20of=20langchain=5Fobject=20to=20reflect=20changes=20=E2=9C=A8?= =?UTF-8?q?=20feat(manager.py):=20add=20reinitialize=5Fservices=20function?= =?UTF-8?q?=20to=20reinitialize=20all=20services=20=E2=9C=A8=20feat(utils.?= =?UTF-8?q?py):=20initialize=20settings=20service=20if=20not=20already=20i?= =?UTF-8?q?nitialized=20before=20returning=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 7 ++--- src/backend/langflow/services/manager.py | 31 ++++++++++++++++++++++ src/backend/langflow/services/utils.py | 9 ++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index e348743ec..b68711828 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -166,12 +166,13 @@ async def process_graph_cached( session_id=session_id, data_graph=data_graph ) # Load the graph using SessionService - langchain_object, artifacts = session_service.load_session(session_id, data_graph) + graph, artifacts = session_service.load_session(session_id, data_graph) + built_object = graph.build() processed_inputs = process_inputs(inputs, artifacts) - result = generate_result(langchain_object, processed_inputs) + result = generate_result(built_object, processed_inputs) # langchain_object is now updated with the new memory # we need to update the cache with the updated langchain_object - session_service.update_session(session_id, (langchain_object, artifacts)) + session_service.update_session(session_id, (graph, artifacts)) return Result(result=result, session_id=session_id) diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index d7215a4cc..59c9aaf8e 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -143,6 +143,37 @@ def initialize_services(): service_service.get(ServiceType.DATABASE_MANAGER) +def reinitialize_services(): + """ + Reinitialize all the services needed. + """ + from langflow.services.database import factory as database_factory + from langflow.services.cache import factory as cache_factory + from langflow.services.chat import factory as chat_factory + from langflow.services.settings import factory as settings_factory + from langflow.services.session import factory as session_service_factory + from langflow.services.auth import factory as auth_factory + from langflow.services.task import factory as task_factory + + service_service.update(ServiceType.SETTINGS_MANAGER) + service_service.update(ServiceType.DATABASE_MANAGER) + service_service.update(ServiceType.CACHE_MANAGER) + service_service.update(ServiceType.CHAT_MANAGER) + service_service.update(ServiceType.SESSION_MANAGER) + service_service.update(ServiceType.AUTH_MANAGER) + service_service.update(ServiceType.TASK_MANAGER) + + # Test cache connection + service_service.get(ServiceType.CACHE_MANAGER) + # Test database connection + service_service.get(ServiceType.DATABASE_MANAGER) + + # Test cache connection + service_service.get(ServiceType.CACHE_MANAGER) + # Test database connection + service_service.get(ServiceType.DATABASE_MANAGER) + + def initialize_settings_service(): """ Initialize the settings manager. diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index aea973e72..edcaa7fe6 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -13,7 +13,14 @@ if TYPE_CHECKING: def get_settings_service() -> "SettingsService": - return service_service.get(ServiceType.SETTINGS_MANAGER) + try: + return service_service.get(ServiceType.SETTINGS_MANAGER) + except ValueError: + # initialize settings service + from langflow.services.manager import initialize_settings_service + + initialize_settings_service() + return service_service.get(ServiceType.SETTINGS_MANAGER) def get_db_service() -> "DatabaseService": From c88f9bf8a0700fc7e8b166b7fb241cb8fb242504 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 11:03:17 -0300 Subject: [PATCH 158/213] =?UTF-8?q?=F0=9F=94=A7=20chore(conftest.py):=20re?= =?UTF-8?q?factor=20client=20fixture=20to=20use=20dependency=20overrides?= =?UTF-8?q?=20for=20session=20and=20add=20session=20fixture=20for=20creati?= =?UTF-8?q?ng=20a=20session=20with=20an=20in-memory=20SQLite=20database=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(conftest.py):=20add=20distributed=5Fenv=20?= =?UTF-8?q?fixture=20to=20set=20up=20environment=20variables=20for=20distr?= =?UTF-8?q?ibuted=20testing=20=F0=9F=94=A7=20chore(conftest.py):=20add=20d?= =?UTF-8?q?istributed=5Fclient=20fixture=20for=20distributed=20testing=20w?= =?UTF-8?q?ith=20Celery=20=F0=9F=94=A7=20chore(conftest.py):=20remove=20un?= =?UTF-8?q?used=20imports=20and=20fixtures=20=F0=9F=94=A7=20chore(test=5Fc?= =?UTF-8?q?ache.py):=20remove=20unused=20client=20fixture=20from=20test=5F?= =?UTF-8?q?build=5Fgraph=20=F0=9F=94=A7=20chore(test=5Fcreators.py):=20rem?= =?UTF-8?q?ove=20unused=20client=20fixture=20from=20test=5Flang=5Fchain=5F?= =?UTF-8?q?type=5Fcreator=5Fto=5Fdict=20=F0=9F=94=A7=20chore(test=5Fdataba?= =?UTF-8?q?se.py):=20remove=20unused=20client=20fixture=20from=20test=5Fdo?= =?UTF-8?q?wnload=5Ffile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 91 +++++++++++++++++++++++++++++------------- tests/test_cache.py | 2 +- tests/test_creators.py | 1 + tests/test_database.py | 6 ++- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index aeaef940a..e58f55ceb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -51,15 +51,75 @@ async def async_client() -> AsyncGenerator: yield client -# Create client fixture for FastAPI -@pytest.fixture(scope="module", autouse=True) -def client(): +@pytest.fixture(name="session") +def session_fixture(): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session + + +@pytest.fixture(name="client") +def client_fixture(session: Session): + def get_session_override(): + return session + from langflow.main import create_app app = create_app() + app.dependency_overrides[get_session] = get_session_override with TestClient(app) as client: yield client + app.dependency_overrides.clear() + + +class Config: + broker_url = "redis://localhost:6379/0" + result_backend = "redis://localhost:6379/0" + + +@pytest.fixture(name="distributed_env") +def setup_env(monkeypatch): + monkeypatch.setenv("LANGFLOW_CACHE_TYPE", "redis") + monkeypatch.setenv("LANGFLOW_REDIS_HOST", "queue") + monkeypatch.setenv("LANGFLOW_REDIS_PORT", "6379") + monkeypatch.setenv("LANGFLOW_REDIS_DB", "0") + monkeypatch.setenv("LANGFLOW_REDIS_EXPIRE", "3600") + monkeypatch.setenv("LANGFLOW_REDIS_PASSWORD", "") + monkeypatch.setenv("FLOWER_UNAUTHENTICATED_API", "True") + monkeypatch.setenv("BROKER_URL", "redis://queue:6379/0") + monkeypatch.setenv("RESULT_BACKEND", "redis://queue:6379/0") + monkeypatch.setenv("C_FORCE_ROOT", "true") + + +@pytest.fixture(name="distributed_client") +def distributed_client_fixture(session: Session, monkeypatch, distributed_env): + # Here we load the .env from ../deploy/.env + from dotenv import load_dotenv + from langflow.services.task import manager + from langflow.core import celery_app + from langflow.services.manager import reinitialize_services, initialize_services + + # monkeypatch langflow.services.task.manager.USE_CELERY to True + monkeypatch.setattr(manager, "USE_CELERY", True) + monkeypatch.setattr( + celery_app, "celery_app", celery_app.make_celery("langflow", Config) + ) + + def get_session_override(): + return session + + from langflow.main import create_app + + app = create_app() + + app.dependency_overrides[get_session] = get_session_override + with TestClient(app) as client: + yield client + app.dependency_overrides.clear() def get_graph(_type="basic"): @@ -119,31 +179,6 @@ def json_vector_store(): return f.read() -@pytest.fixture(name="session") -def session_fixture(): - engine = create_engine( - "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool - ) - SQLModel.metadata.create_all(engine) - with Session(engine) as session: - yield session - - -@pytest.fixture(name="client") -def client_fixture(session: Session): - def get_session_override(): - return session - - from langflow.main import create_app - - app = create_app() - - app.dependency_overrides[get_session] = get_session_override - with TestClient(app) as client: - yield client - app.dependency_overrides.clear() - - # @contextmanager # def session_getter(): # try: diff --git a/tests/test_cache.py b/tests/test_cache.py index 4ceea2c2a..c2c706ee9 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -38,7 +38,7 @@ def langchain_objects_are_equal(obj1, obj2): # Test build_graph -def test_build_graph(basic_data_graph): +def test_build_graph(client, basic_data_graph): graph = Graph.from_payload(basic_data_graph) assert graph is not None assert len(graph.nodes) == len(basic_data_graph["nodes"]) diff --git a/tests/test_creators.py b/tests/test_creators.py index 2098e87cd..177dd4105 100644 --- a/tests/test_creators.py +++ b/tests/test_creators.py @@ -32,6 +32,7 @@ def sample_agent_creator() -> AgentCreator: def test_lang_chain_type_creator_to_dict( + client, sample_lang_chain_type_creator: LangChainTypeCreator, ): type_dict = sample_lang_chain_type_creator.to_dict() diff --git a/tests/test_database.py b/tests/test_database.py index e4f68ca56..058c470c5 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -179,7 +179,11 @@ def test_upload_file( def test_download_file( - client: TestClient, session: Session, json_flow, active_user, logged_in_headers + client: TestClient, + session: Session, + json_flow, + active_user, + logged_in_headers, ): flow = orjson.loads(json_flow) data = flow["data"] From 1f720a665eee86d5e6aa70d0e85a6730bd78f261 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 11:03:54 -0300 Subject: [PATCH 159/213] =?UTF-8?q?=F0=9F=94=A7=20fix(test=5Fendpoints.py)?= =?UTF-8?q?:=20fix=20typo=20in=20test=5Fprocess=5Fflow=5Fwithout=5Fautolog?= =?UTF-8?q?in=20function=20to=20improve=20code=20readability=20=E2=9C=A8?= =?UTF-8?q?=20feat(test=5Fendpoints.py):=20add=20helper=20functions=20run?= =?UTF-8?q?=5Fpost=20and=20poll=5Ftask=5Fstatus=20to=20improve=20code=20mo?= =?UTF-8?q?dularity=20and=20reusability=20=F0=9F=94=A7=20fix(test=5Fendpoi?= =?UTF-8?q?nts.py):=20fix=20typo=20in=20test=5Fbasic=5Fchat=5Fwith=5Ftwo?= =?UTF-8?q?=5Fsession=5Fids=5Fand=5Fnames=20function=20to=20improve=20code?= =?UTF-8?q?=20readability=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20add?= =?UTF-8?q?=20async=5Ftest=20marker=20to=20test=5Fvector=5Fstore=5Fin=5Fpr?= =?UTF-8?q?ocess=20function=20to=20indicate=20it=20is=20an=20asynchronous?= =?UTF-8?q?=20test=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20distri?= =?UTF-8?q?buted=5Fclient=20parameter=20to=20test=5Fvector=5Fstore=5Fin=5F?= =?UTF-8?q?process=20function=20to=20test=20distributed=20client=20functio?= =?UTF-8?q?nality=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20async?= =?UTF-8?q?=5Ftest=20marker=20to=20test=5Fasync=5Ftask=5Fprocessing=20func?= =?UTF-8?q?tion=20to=20indicate=20it=20is=20an=20asynchronous=20test=20?= =?UTF-8?q?=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20distributed=5Fcl?= =?UTF-8?q?ient=20parameter=20to=20test=5Fasync=5Ftask=5Fprocessing=20func?= =?UTF-8?q?tion=20to=20test=20distributed=20client=20functionality=20?= =?UTF-8?q?=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20async=5Ftest=20m?= =?UTF-8?q?arker=20to=20test=5Fasync=5Ftask=5Fprocessing=5Fvector=5Fstore?= =?UTF-8?q?=20function=20to=20indicate=20it=20is=20an=20asynchronous=20tes?= =?UTF-8?q?t=20=E2=9C=A8=20feat(test=5Fendpoints.py):=20add=20distributed?= =?UTF-8?q?=5Fclient=20parameter=20to=20test=5Fasync=5Ftask=5Fprocessing?= =?UTF-8?q?=5Fvector=5Fstore=20function=20to=20test=20distributed=20client?= =?UTF-8?q?=20functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_endpoints.py | 47 +++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 8f16e11b5..12d2340a2 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -8,7 +8,33 @@ from fastapi.testclient import TestClient from langflow.interface.tools.constants import CUSTOM_TOOLS from langflow.template.frontend_node.chains import TimeTravelGuideChainNode -from tests.utils import poll_task_status, run_post +import time + + +def run_post(client, flow_id, headers, post_data): + response = client.post( + f"api/v1/process/{flow_id}", + headers=headers, + json=post_data, + ) + assert response.status_code == 200, response.json() + return response.json() + + +# Helper function to poll task status +def poll_task_status(client, headers, task_id, max_attempts=20, sleep_time=1): + for _ in range(max_attempts): + task_status_response = client.get( + f"api/v1/task/{task_id}/status", + headers=headers, + ) + if ( + task_status_response.status_code == 200 + and task_status_response.json()["status"] == "SUCCESS" + ): + return task_status_response.json() + time.sleep(sleep_time) + return None # Return None if task did not complete in time PROMPT_REQUEST = { @@ -187,7 +213,7 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k # Dummy POST data post_data = { - "inputs": {"key": "value"}, + "inputs": {"input": "value"}, "tweaks": None, "clear_cache": False, "session_id": None, @@ -451,11 +477,14 @@ def test_basic_chat_with_two_session_ids_and_names(client, added_flow, created_a assert name in response_json["result"]["text"] -def test_vector_store_in_process(client, added_vector_store, created_api_key): +@pytest.mark.async_test +def test_vector_store_in_process( + distributed_client, added_vector_store, created_api_key +): # Run the /api/v1/process/{flow_id} endpoint headers = {"x-api-key": created_api_key.api_key} post_data = {"inputs": {"input": "What is Langflow?"}} - response = client.post( + response = distributed_client.post( f"api/v1/process/{added_vector_store.get('id')}", headers=headers, json=post_data, @@ -470,12 +499,12 @@ def test_vector_store_in_process(client, added_vector_store, created_api_key): # Test function without loop @pytest.mark.async_test -def test_async_task_processing(client, added_flow, created_api_key): +def test_async_task_processing(distributed_client, added_flow, created_api_key): headers = {"x-api-key": created_api_key.api_key} post_data = {"inputs": {"text": "Hi, My name is Gabriel"}} # Run the /api/v1/process/{flow_id} endpoint with sync=False - response = client.post( + response = distributed_client.post( f"api/v1/process/{added_flow.get('id')}", headers=headers, json={**post_data, "sync": False}, @@ -487,7 +516,7 @@ def test_async_task_processing(client, added_flow, created_api_key): assert task_id is not None # Polling the task status using the helper function - task_status_json = poll_task_status(client, headers, task_id) + task_status_json = poll_task_status(distributed_client, headers, task_id) assert task_status_json is not None, "Task did not complete in time" # Validate that the task completed successfully and the result is as expected @@ -502,7 +531,7 @@ def test_async_task_processing_vector_store( client, added_vector_store, created_api_key ): headers = {"x-api-key": created_api_key.api_key} - post_data = {"inputs": {"input": "What is Langflow?"}} + post_data = {"inputs": {"input": "How do I upload examples?"}} # Run the /api/v1/process/{flow_id} endpoint with sync=False response = client.post( @@ -525,6 +554,6 @@ def test_async_task_processing_vector_store( # Validate that the task completed successfully and the result is as expected assert "result" in task_status_json, task_status_json assert "output" in task_status_json["result"], task_status_json["result"] - assert "Langflow is" in task_status_json["result"]["output"], task_status_json[ + assert "Langflow" in task_status_json["result"]["output"], task_status_json[ "result" ] From 59b62aae17cf6b055469ef24c5ae8c9e6a5b166e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 11:04:57 -0300 Subject: [PATCH 160/213] =?UTF-8?q?=F0=9F=94=A8=20refactor(test=5Fgraph.py?= =?UTF-8?q?):=20remove=20unused=20imports=20and=20variables=20to=20improve?= =?UTF-8?q?=20code=20readability=20and=20maintainability=20=F0=9F=94=A7=20?= =?UTF-8?q?chore(test=5Fgraph.py):=20add=20missing=20imports=20for=20Agent?= =?UTF-8?q?Vertex=20and=20VectorStoreVertex=20to=20fix=20NameError=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20import?= =?UTF-8?q?=20for=20langchain.agents.AgentExecutor=20to=20fix=20NameError?= =?UTF-8?q?=20=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20imp?= =?UTF-8?q?ort=20for=20langchain.graph.Graph=20to=20fix=20NameError=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20import?= =?UTF-8?q?=20for=20langflow.processing.process.get=5Fresult=5Fand=5Fthoug?= =?UTF-8?q?ht=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(test=5Fgraph.p?= =?UTF-8?q?y):=20add=20missing=20import=20for=20langflow.utils.payload.get?= =?UTF-8?q?=5Froot=5Fnode=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(te?= =?UTF-8?q?st=5Fgraph.py):=20add=20missing=20import=20for=20langchain.llms?= =?UTF-8?q?.fake.FakeListLLM=20to=20fix=20NameError=20=F0=9F=94=A7=20chore?= =?UTF-8?q?(test=5Fgraph.py):=20add=20missing=20import=20for=20langchain.c?= =?UTF-8?q?hains.base.Chain=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(?= =?UTF-8?q?test=5Fgraph.py):=20add=20missing=20import=20for=20langflow.gra?= =?UTF-8?q?ph.edge.base.Edge=20to=20fix=20NameError=20=F0=9F=94=A7=20chore?= =?UTF-8?q?(test=5Fgraph.py):=20add=20missing=20import=20for=20langflow.gr?= =?UTF-8?q?aph.vertex.base.Vertex=20to=20fix=20NameError=20=F0=9F=94=A7=20?= =?UTF-8?q?chore(test=5Fgraph.py):=20add=20missing=20import=20for=20langfl?= =?UTF-8?q?ow.graph.vertex.types.ToolkitVertex=20to=20fix=20NameError=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20import?= =?UTF-8?q?=20for=20langflow.graph.vertex.types.FileToolVertex=20to=20fix?= =?UTF-8?q?=20NameError=20=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20m?= =?UTF-8?q?issing=20import=20for=20langflow.graph.vertex.types.LLMVertex?= =?UTF-8?q?=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(test=5Fgraph.py)?= =?UTF-8?q?:=20add=20missing=20import=20for=20langflow.graph.vertex.types.?= =?UTF-8?q?AgentVertex=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(test?= =?UTF-8?q?=5Fgraph.py):=20add=20missing=20import=20for=20langflow.graph.v?= =?UTF-8?q?ertex.types.VectorStoreVertex=20to=20fix=20NameError=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20import?= =?UTF-8?q?=20for=20langchain.agents.AgentExecutor=20to=20fix=20NameError?= =?UTF-8?q?=20=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20imp?= =?UTF-8?q?ort=20for=20langchain.graph.Graph=20to=20fix=20NameError=20?= =?UTF-8?q?=F0=9F=94=A7=20chore(test=5Fgraph.py):=20add=20missing=20import?= =?UTF-8?q?=20for=20langflow.processing.process.get=5Fresult=5Fand=5Fthoug?= =?UTF-8?q?ht=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(test=5Fgraph.p?= =?UTF-8?q?y):=20add=20missing=20import=20for=20langflow.utils.payload.get?= =?UTF-8?q?=5Froot=5Fnode=20to=20fix=20NameError=20=F0=9F=94=A7=20chore(te?= =?UTF-8?q?st=5Fgraph.py):=20add=20missing=20import=20for=20langchain.llms?= =?UTF-8?q?.fake.FakeListLLM=20to=20fix=20NameError=20=F0=9F=94=A7=20chore?= =?UTF-8?q?(test=5Fgraph.py):=20add=20missing=20import=20for=20langchain.c?= =?UTF-8?q?hains.base.Chain=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_graph.py | 39 +++++++++++++++++++++++++++++++++++---- tests/test_process.py | 33 ++++++++++----------------------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/tests/test_graph.py b/tests/test_graph.py index f3efe3614..f88d89a1b 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -1,17 +1,22 @@ +import json import os from pathlib import Path +import pickle from typing import Type, Union +from langflow import graph from langflow.graph.edge.base import Edge from langflow.graph.vertex.base import Vertex - +from langchain.agents import AgentExecutor import pytest from langchain.chains.base import Chain from langchain.llms.fake import FakeListLLM from langflow.graph import Graph from langflow.graph.vertex.types import ( + AgentVertex, FileToolVertex, LLMVertex, ToolkitVertex, + VectorStoreVertex, ) from langflow.processing.process import get_result_and_thought from langflow.utils.payload import get_root_node @@ -185,7 +190,7 @@ def test_build_edges(basic_graph): assert isinstance(edge.target, Vertex) -def test_get_root_node(basic_graph, complex_graph): +def test_get_root_node(client, basic_graph, complex_graph): """Test getting root node""" assert isinstance(basic_graph, Graph) root = get_root_node(basic_graph) @@ -261,7 +266,7 @@ def test_llm_node_build(basic_graph): assert built_object is not None -def test_toolkit_node_build(openapi_graph): +def test_toolkit_node_build(client, openapi_graph): # Write a file to the disk file_path = "api-with-examples.yaml" with open(file_path, "w") as f: @@ -276,7 +281,7 @@ def test_toolkit_node_build(openapi_graph): assert not Path(file_path).exists() -def test_file_tool_node_build(openapi_graph): +def test_file_tool_node_build(client, openapi_graph): file_path = "api-with-examples.yaml" with open(file_path, "w") as f: f.write("openapi: 3.0.0") @@ -318,3 +323,29 @@ def test_get_result_and_thought(basic_graph): # Get the result and thought result = get_result_and_thought(langchain_object, message) assert isinstance(result, dict) + + +def test_pickle_graph(json_vector_store): + loaded_json = json.loads(json_vector_store) + graph = Graph.from_payload(loaded_json) + assert isinstance(graph, Graph) + first_result = graph.build() + assert isinstance(first_result, AgentExecutor) + pickled = pickle.dumps(graph) + assert pickled is not None + unpickled = pickle.loads(pickled) + assert unpickled is not None + result = unpickled.build() + assert isinstance(result, AgentExecutor) + + +def test_pickle_each_vertex(json_vector_store): + loaded_json = json.loads(json_vector_store) + graph = Graph.from_payload(loaded_json) + assert isinstance(graph, Graph) + for vertex in graph.nodes: + vertex.build() + pickled = pickle.dumps(vertex) + assert pickled is not None + unpickled = pickle.loads(pickled) + assert unpickled is not None diff --git a/tests/test_process.py b/tests/test_process.py index bb0147616..775d17145 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -201,15 +201,11 @@ def test_load_langchain_object_with_cached_session(client, basic_graph_data): # Provide a non-existent session_id session_service = get_session_service() session_id1 = "non-existent-session-id" - langchain_object1, artifacts1 = session_service.load_session( - session_id1, basic_graph_data - ) + graph1, artifacts1 = session_service.load_session(session_id1, basic_graph_data) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2 = session_service.load_session( - session_id1, basic_graph_data - ) + graph2, artifacts2 = session_service.load_session(session_id1, basic_graph_data) - assert id(langchain_object1) == id(langchain_object2) + assert graph1 == graph2 assert artifacts1 == artifacts2 @@ -218,31 +214,22 @@ def test_load_langchain_object_with_no_cached_session(client, basic_graph_data): session_service = get_session_service() session_id1 = "non-existent-session-id" session_id = session_service.build_key(session_id1, basic_graph_data) - langchain_object1, artifacts1 = session_service.load_session( - session_id, basic_graph_data - ) + graph1, artifacts1 = session_service.load_session(session_id, basic_graph_data) # Clear the cache session_service.clear_session(session_id) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2 = session_service.load_session( - session_id, basic_graph_data - ) + graph2, artifacts2 = session_service.load_session(session_id, basic_graph_data) - assert id(langchain_object1) != id( - langchain_object2 - ) # Since the cache was cleared, objects should be different + assert id(graph1) != id(graph2) + # Since the cache was cleared, objects should be different def test_load_langchain_object_without_session_id(client, basic_graph_data): # Provide a non-existent session_id session_service = get_session_service() session_id1 = None - langchain_object1, artifacts1 = session_service.load_session( - session_id1, basic_graph_data - ) + graph1, artifacts1 = session_service.load_session(session_id1, basic_graph_data) # Use the new session_id to get the langchain_object again - langchain_object2, artifacts2 = session_service.load_session( - session_id1, basic_graph_data - ) + graph2, artifacts2 = session_service.load_session(session_id1, basic_graph_data) - assert id(langchain_object1) == id(langchain_object2) + assert graph1 == graph2 From 416d2941174c8faf4672d607cf4d348f5550076d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 11:06:25 -0300 Subject: [PATCH 161/213] =?UTF-8?q?=F0=9F=90=9B=20fix(basic=5Fexample.json?= =?UTF-8?q?):=20change=20value=20of=20"value"=20key=20from=20"abc"=20to=20?= =?UTF-8?q?null=20to=20remove=20hardcoded=20value=20and=20improve=20flexib?= =?UTF-8?q?ility=20=E2=9C=85=20test(test=5Fuser.py):=20add=20test=20to=20c?= =?UTF-8?q?reate=20a=20super=20user=20for=20testing=20purposes=20and=20ens?= =?UTF-8?q?ure=20it=20returns=20the=20correct=20response=20=F0=9F=94=A5=20?= =?UTF-8?q?chore(utils.py):=20remove=20unused=20functions=20run=5Fpost=20a?= =?UTF-8?q?nd=20poll=5Ftask=5Fstatus=20to=20clean=20up=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/data/basic_example.json | 2 +- tests/test_user.py | 4 +++- tests/utils.py | 27 --------------------------- 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/tests/data/basic_example.json b/tests/data/basic_example.json index 2c08512bb..35f515bc4 100644 --- a/tests/data/basic_example.json +++ b/tests/data/basic_example.json @@ -236,7 +236,7 @@ "placeholder": "", "show": true, "multiline": false, - "value": "abc", + "value": null, "password": true, "name": "openai_api_key", "display_name": "OpenAI API Key", diff --git a/tests/test_user.py b/tests/test_user.py index 9c4153316..c0a020554 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -213,7 +213,9 @@ def test_normal_user_cant_delete_user(client, test_user, logged_in_headers): # If you still want to test the superuser endpoint -def test_add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(client): +def test_add_super_user_for_testing_purposes_delete_me_before_merge_into_dev( + client, +): response = client.post("/api/v1/super_user") assert response.status_code == 200 assert response.json()["username"] == "superuser" diff --git a/tests/utils.py b/tests/utils.py index a4bae9863..e69de29bb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,27 +0,0 @@ -import time - - -def run_post(client, flow_id, headers, post_data): - response = client.post( - f"api/v1/process/{flow_id}", - headers=headers, - json=post_data, - ) - assert response.status_code == 200, response.json() - return response.json() - - -# Helper function to poll task status -def poll_task_status(client, headers, task_id, max_attempts=20, sleep_time=1): - for _ in range(max_attempts): - task_status_response = client.get( - f"api/v1/task/{task_id}/status", - headers=headers, - ) - if ( - task_status_response.status_code == 200 - and task_status_response.json()["status"] == "SUCCESS" - ): - return task_status_response.json() - time.sleep(sleep_time) - return None # Return None if task did not complete in time From dcd9d3a23ec2701acc2852366f9f35f014595daf Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 13:08:57 -0300 Subject: [PATCH 162/213] =?UTF-8?q?=F0=9F=90=9B=20fix(=5F=5Finit=5F=5F.py)?= =?UTF-8?q?:=20rename=20service=5Fservice=20to=20service=5Fmanager=20for?= =?UTF-8?q?=20better=20semantics=20=F0=9F=90=9B=20fix(manager.py):=20renam?= =?UTF-8?q?e=20service=5Fservice=20to=20service=5Fmanager=20for=20better?= =?UTF-8?q?=20semantics=20=F0=9F=90=9B=20fix(manager.py):=20rename=20Servi?= =?UTF-8?q?ceService=20class=20to=20ServiceManager=20for=20better=20semant?= =?UTF-8?q?ics=20=F0=9F=90=9B=20fix(manager.py):=20rename=20service=5Fserv?= =?UTF-8?q?ice=20variable=20to=20service=5Fmanager=20for=20better=20semant?= =?UTF-8?q?ics=20=F0=9F=90=9B=20fix(utils.py):=20rename=20service=5Fservic?= =?UTF-8?q?e=20to=20service=5Fmanager=20for=20better=20semantics=20?= =?UTF-8?q?=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20service?= =?UTF-8?q?=5Fmanager=20to=20manage=20creation=20of=20different=20services?= =?UTF-8?q?=20=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20servic?= =?UTF-8?q?e=5Fmanager=20to=20update=20services=20=E2=9C=A8=20feat(manager?= =?UTF-8?q?.py):=20add=20support=20for=20service=5Fmanager=20to=20teardown?= =?UTF-8?q?=20services=20=E2=9C=A8=20feat(manager.py):=20add=20support=20f?= =?UTF-8?q?or=20service=5Fmanager=20to=20register=20and=20update=20factori?= =?UTF-8?q?es=20=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20serv?= =?UTF-8?q?ice=5Fmanager=20to=20initialize=20and=20reinitialize=20services?= =?UTF-8?q?=20=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20servic?= =?UTF-8?q?e=5Fmanager=20to=20get=20services=20by=20type=20=E2=9C=A8=20fea?= =?UTF-8?q?t(manager.py):=20add=20support=20for=20service=5Fmanager=20to?= =?UTF-8?q?=20get=20services=20by=20type=20=E2=9C=A8=20feat(manager.py):?= =?UTF-8?q?=20add=20support=20for=20service=5Fmanager=20to=20get=20service?= =?UTF-8?q?s=20by=20type=20=E2=9C=A8=20feat(manager.py):=20add=20support?= =?UTF-8?q?=20for=20service=5Fmanager=20to=20get=20services=20by=20type=20?= =?UTF-8?q?=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20service?= =?UTF-8?q?=5Fmanager=20to=20get=20services=20by=20type=20=E2=9C=A8=20feat?= =?UTF-8?q?(manager.py):=20add=20support=20for=20service=5Fmanager=20to=20?= =?UTF-8?q?get=20services=20by=20type=20=E2=9C=A8=20feat(manager.py):=20ad?= =?UTF-8?q?d=20support=20for=20service=5Fmanager=20to=20get=20services=20b?= =?UTF-8?q?y=20type=20=E2=9C=A8=20feat(manager.py):=20add=20support=20for?= =?UTF-8?q?=20service=5Fmanager=20to=20get=20services=20by=20type=20?= =?UTF-8?q?=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20service?= =?UTF-8?q?=5Fmanager=20to=20get=20services=20by=20type=20=E2=9C=A8=20feat?= =?UTF-8?q?(manager.py):=20add=20support=20for=20service=5Fmanager=20to=20?= =?UTF-8?q?get=20services=20by=20type=20=E2=9C=A8=20feat(manager.py):=20ad?= =?UTF-8?q?d=20support=20for=20service=5Fmanager=20to=20get=20services=20b?= =?UTF-8?q?y=20type=20=E2=9C=A8=20feat(manager.py):=20add=20support=20for?= =?UTF-8?q?=20service=5Fmanager=20to=20get=20services=20by=20type=20?= =?UTF-8?q?=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20service?= =?UTF-8?q?=5Fmanager=20to=20get=20services=20by=20type=20=E2=9C=A8=20feat?= =?UTF-8?q?(manager.py):=20add=20support=20for=20service=5Fmanager=20to=20?= =?UTF-8?q?get=20services=20by=20type=20=E2=9C=A8=20feat(manager.py):=20ad?= =?UTF-8?q?d=20support=20for=20service=5Fmanager=20to=20get=20services=20b?= =?UTF-8?q?y=20type=20=E2=9C=A8=20feat(manager.py):=20add=20support=20for?= =?UTF-8?q?=20service=5Fmanager=20to=20get=20services=20by=20type=20?= =?UTF-8?q?=E2=9C=A8=20feat(manager.py):=20add=20support=20for=20service?= =?UTF-8?q?=5Fmanager=20to=20get=20services=20by=20type=20=E2=9C=A8=20feat?= =?UTF-8?q?(manager.py):=20add=20support=20for=20service=5Fmanager=20to=20?= =?UTF-8?q?get=20services=20by=20type=20=E2=9C=A8=20feat(manager.py):=20ad?= =?UTF-8?q?d=20support=20for=20service=5Fmanager=20to=20get=20services=20b?= =?UTF-8?q?y=20type=20=E2=9C=A8=20feat(manager.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/__init__.py | 4 +- src/backend/langflow/services/chat/manager.py | 4 +- .../langflow/services/database/utils.py | 4 +- src/backend/langflow/services/manager.py | 56 +++++++++---------- .../langflow/services/plugins/langfuse.py | 4 +- src/backend/langflow/services/utils.py | 18 +++--- 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/backend/langflow/services/__init__.py b/src/backend/langflow/services/__init__.py index c58f34b1d..8ac74b5b9 100644 --- a/src/backend/langflow/services/__init__.py +++ b/src/backend/langflow/services/__init__.py @@ -1,4 +1,4 @@ -from .manager import service_service +from .manager import service_manager from .schema import ServiceType -__all__ = ["service_service", "ServiceType"] +__all__ = ["service_manager", "ServiceType"] diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index 2a2e1ce42..029c26ecd 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -12,7 +12,7 @@ from .cache import cache_service import asyncio from typing import Any, Dict, List -from langflow.services import service_service, ServiceType +from langflow.services import service_manager, ServiceType import orjson @@ -52,7 +52,7 @@ class ChatService(Service): self.chat_history = ChatHistory() self.chat_cache = cache_service self.chat_cache.attach(self.update) - self.cache_service = service_service.get(ServiceType.CACHE_MANAGER) + self.cache_service = service_manager.get(ServiceType.CACHE_MANAGER) def on_chat_history_update(self): """Send the last chat message to the client.""" diff --git a/src/backend/langflow/services/database/utils.py b/src/backend/langflow/services/database/utils.py index db4fdf124..5ac62ee93 100644 --- a/src/backend/langflow/services/database/utils.py +++ b/src/backend/langflow/services/database/utils.py @@ -11,9 +11,9 @@ if TYPE_CHECKING: def initialize_database(): logger.debug("Initializing database") - from langflow.services import service_service, ServiceType + from langflow.services import service_manager, ServiceType - database_service = service_service.get(ServiceType.DATABASE_MANAGER) + database_service = service_manager.get(ServiceType.DATABASE_MANAGER) try: database_service.check_schema_health() except Exception as exc: diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index 59c9aaf8e..e6d8aabd1 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from langflow.services.base import Service -class ServiceService: +class ServiceManager: """ Manages the creation of different services. """ @@ -95,7 +95,7 @@ class ServiceService: self.dependencies = {} -service_service = ServiceService() +service_manager = ServiceManager() def initialize_services(): @@ -110,37 +110,37 @@ def initialize_services(): from langflow.services.auth import factory as auth_factory from langflow.services.task import factory as task_factory - service_service.register_factory(settings_factory.SettingsServiceFactory()) - service_service.register_factory( + service_manager.register_factory(settings_factory.SettingsServiceFactory()) + service_manager.register_factory( database_factory.DatabaseServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER], ) - service_service.register_factory( + service_manager.register_factory( cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] ) - service_service.register_factory( + service_manager.register_factory( auth_factory.AuthServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] ) - service_service.register_factory(chat_factory.ChatServiceFactory()) - service_service.register_factory( + service_manager.register_factory(chat_factory.ChatServiceFactory()) + service_manager.register_factory( session_service_factory.SessionServiceFactory(), dependencies=[ServiceType.CACHE_MANAGER], ) - service_service.register_factory( + service_manager.register_factory( task_factory.TaskServiceFactory(), ) # Test cache connection - service_service.get(ServiceType.CACHE_MANAGER) + service_manager.get(ServiceType.CACHE_MANAGER) # Test database connection - service_service.get(ServiceType.DATABASE_MANAGER) + service_manager.get(ServiceType.DATABASE_MANAGER) # Test cache connection - service_service.get(ServiceType.CACHE_MANAGER) + service_manager.get(ServiceType.CACHE_MANAGER) # Test database connection - service_service.get(ServiceType.DATABASE_MANAGER) + service_manager.get(ServiceType.DATABASE_MANAGER) def reinitialize_services(): @@ -155,23 +155,23 @@ def reinitialize_services(): from langflow.services.auth import factory as auth_factory from langflow.services.task import factory as task_factory - service_service.update(ServiceType.SETTINGS_MANAGER) - service_service.update(ServiceType.DATABASE_MANAGER) - service_service.update(ServiceType.CACHE_MANAGER) - service_service.update(ServiceType.CHAT_MANAGER) - service_service.update(ServiceType.SESSION_MANAGER) - service_service.update(ServiceType.AUTH_MANAGER) - service_service.update(ServiceType.TASK_MANAGER) + service_manager.update(ServiceType.SETTINGS_MANAGER) + service_manager.update(ServiceType.DATABASE_MANAGER) + service_manager.update(ServiceType.CACHE_MANAGER) + service_manager.update(ServiceType.CHAT_MANAGER) + service_manager.update(ServiceType.SESSION_MANAGER) + service_manager.update(ServiceType.AUTH_MANAGER) + service_manager.update(ServiceType.TASK_MANAGER) # Test cache connection - service_service.get(ServiceType.CACHE_MANAGER) + service_manager.get(ServiceType.CACHE_MANAGER) # Test database connection - service_service.get(ServiceType.DATABASE_MANAGER) + service_manager.get(ServiceType.DATABASE_MANAGER) # Test cache connection - service_service.get(ServiceType.CACHE_MANAGER) + service_manager.get(ServiceType.CACHE_MANAGER) # Test database connection - service_service.get(ServiceType.DATABASE_MANAGER) + service_manager.get(ServiceType.DATABASE_MANAGER) def initialize_settings_service(): @@ -180,7 +180,7 @@ def initialize_settings_service(): """ from langflow.services.settings import factory as settings_factory - service_service.register_factory(settings_factory.SettingsServiceFactory()) + service_manager.register_factory(settings_factory.SettingsServiceFactory()) def initialize_session_service(): @@ -192,11 +192,11 @@ def initialize_session_service(): initialize_settings_service() - service_service.register_factory( + service_manager.register_factory( cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] ) - service_service.register_factory( + service_manager.register_factory( session_service_factory.SessionServiceFactory(), dependencies=[ServiceType.CACHE_MANAGER], ) @@ -206,4 +206,4 @@ def teardown_services(): """ Teardown all the services. """ - service_service.teardown() + service_manager.teardown() diff --git a/src/backend/langflow/services/plugins/langfuse.py b/src/backend/langflow/services/plugins/langfuse.py index ce3e25c53..bc50ccc2c 100644 --- a/src/backend/langflow/services/plugins/langfuse.py +++ b/src/backend/langflow/services/plugins/langfuse.py @@ -1,4 +1,4 @@ -from langflow.services.utils import get_settings_manager +from langflow.services.utils import get_settings_service from langflow.utils.logger import logger ### Temporary implementation @@ -20,7 +20,7 @@ class LangfuseInstance: logger.debug("Creating Langfuse instance") from langfuse import Langfuse # type: ignore - settings_manager = get_settings_manager() + settings_manager = get_settings_service() if ( settings_manager.settings.LANGFUSE_PUBLIC_KEY diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index edcaa7fe6..9eb7ce66a 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -1,4 +1,4 @@ -from langflow.services import ServiceType, service_service +from langflow.services import ServiceType, service_manager from typing import TYPE_CHECKING, Generator @@ -14,35 +14,35 @@ if TYPE_CHECKING: def get_settings_service() -> "SettingsService": try: - return service_service.get(ServiceType.SETTINGS_MANAGER) + return service_manager.get(ServiceType.SETTINGS_MANAGER) except ValueError: # initialize settings service from langflow.services.manager import initialize_settings_service initialize_settings_service() - return service_service.get(ServiceType.SETTINGS_MANAGER) + return service_manager.get(ServiceType.SETTINGS_MANAGER) def get_db_service() -> "DatabaseService": - return service_service.get(ServiceType.DATABASE_MANAGER) + return service_manager.get(ServiceType.DATABASE_MANAGER) def get_session() -> Generator["Session", None, None]: - db_service = service_service.get(ServiceType.DATABASE_MANAGER) + db_service = service_manager.get(ServiceType.DATABASE_MANAGER) yield from db_service.get_session() def get_cache_service() -> "BaseCacheService": - return service_service.get(ServiceType.CACHE_MANAGER) + return service_manager.get(ServiceType.CACHE_MANAGER) def get_session_service() -> "SessionService": - return service_service.get(ServiceType.SESSION_MANAGER) + return service_manager.get(ServiceType.SESSION_MANAGER) def get_task_service() -> "TaskService": - return service_service.get(ServiceType.TASK_MANAGER) + return service_manager.get(ServiceType.TASK_MANAGER) def get_chat_service() -> "ChatService": - return service_service.get(ServiceType.CHAT_MANAGER) + return service_manager.get(ServiceType.CHAT_MANAGER) From e676e3631a304cc3db7e2b9b223013d463dbebc5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 14:02:30 -0300 Subject: [PATCH 163/213] =?UTF-8?q?=F0=9F=94=A7=20fix(conftest.py):=20set?= =?UTF-8?q?=20LANGFLOW=5FAUTO=5FLOGIN=20environment=20variable=20to=20Fals?= =?UTF-8?q?e=20in=20client=5Ffixture=20and=20distributed=5Fclient=5Ffixtur?= =?UTF-8?q?e=20to=20disable=20auto=20login=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 63af8b473..0109845af 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -62,10 +62,11 @@ def session_fixture(): @pytest.fixture(name="client") -def client_fixture(session: Session): +def client_fixture(session: Session, monkeypatch): def get_session_override(): return session + monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", False) from langflow.main import create_app app = create_app() @@ -104,6 +105,7 @@ def distributed_client_fixture(session: Session, monkeypatch, distributed_env): from langflow.services.manager import reinitialize_services, initialize_services # monkeypatch langflow.services.task.manager.USE_CELERY to True + monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", False) monkeypatch.setattr(manager, "USE_CELERY", True) monkeypatch.setattr( celery_app, "celery_app", celery_app.make_celery("langflow", Config) @@ -120,6 +122,7 @@ def distributed_client_fixture(session: Session, monkeypatch, distributed_env): with TestClient(app) as client: yield client app.dependency_overrides.clear() + monkeypatch.undo() def get_graph(_type="basic"): From df9c8c05b5c89ae590b47c033c718b9f425e71e1 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 14:03:00 -0300 Subject: [PATCH 164/213] =?UTF-8?q?=F0=9F=94=A7=20refactor(utils.py):=20re?= =?UTF-8?q?move=20unused=20functions=20merge=5Fnested=5Fdicts=20and=20merg?= =?UTF-8?q?e=5Fnested=5Fdicts=5Fwith=5Frenaming=20to=20improve=20code=20re?= =?UTF-8?q?adability=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 refactor(endpoints.py): remove unused imports and functions build_langchain_types_dict, build_langchain_template_custom_component, and build_langchain_custom_component_list_from_path to improve code readability and maintainability ✨ feat(endpoints.py): add error handling to get_all endpoint to return a 500 status code with the exception message if an error occurs during the retrieval of langchain types dict --- src/backend/langflow/api/utils.py | 27 -------------- src/backend/langflow/api/v1/endpoints.py | 47 ++++-------------------- 2 files changed, 7 insertions(+), 67 deletions(-) diff --git a/src/backend/langflow/api/utils.py b/src/backend/langflow/api/utils.py index 0fb53e541..c519fffed 100644 --- a/src/backend/langflow/api/utils.py +++ b/src/backend/langflow/api/utils.py @@ -59,33 +59,6 @@ def build_input_keys_response(langchain_object, artifacts): return input_keys_response -def merge_nested_dicts(dict1, dict2): - for key, value in dict2.items(): - if isinstance(value, dict) and isinstance(dict1.get(key), dict): - dict1[key] = merge_nested_dicts(dict1[key], value) - else: - dict1[key] = value - return dict1 - - -def merge_nested_dicts_with_renaming(dict1, dict2): - for key, value in dict2.items(): - if ( - key in dict1 - and isinstance(value, dict) - and isinstance(dict1.get(key), dict) - ): - for sub_key, sub_value in value.items(): - if sub_key in dict1[key]: - new_key = get_new_key(dict1[key], sub_key) - dict1[key][new_key] = sub_value - else: - dict1[key][sub_key] = sub_value - else: - dict1[key] = value - return dict1 - - def get_new_key(dictionary, original_key): counter = 1 new_key = original_key + " (" + str(counter) + ")" diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index e4e22bf7d..274fe26ff 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -1,5 +1,5 @@ from http import HTTPStatus -from typing import Annotated, Any, Optional, Union +from typing import Annotated, Optional, Union from langflow.services.auth.utils import api_key_security, get_current_active_user @@ -21,12 +21,9 @@ from langflow.api.v1.schemas import ( CustomComponentCode, ) -from langflow.api.utils import merge_nested_dicts_with_renaming from langflow.interface.types import ( - build_langchain_types_dict, build_langchain_template_custom_component, - build_langchain_custom_component_list_from_path, ) from langflow.services.utils import get_session @@ -52,43 +49,13 @@ router = APIRouter(tags=["Base"]) def get_all( settings_service=Depends(get_settings_service), ): + from langflow.interface.types import get_all_types_dict + logger.debug("Building langchain types dict") - native_components = build_langchain_types_dict() - # custom_components is a list of dicts - # need to merge all the keys into one dict - custom_components_from_file: dict[str, Any] = {} - if settings_service.settings.COMPONENTS_PATH: - logger.info( - f"Building custom components from {settings_service.settings.COMPONENTS_PATH}" - ) - - custom_component_dicts = [] - processed_paths = [] - for path in settings_service.settings.COMPONENTS_PATH: - if str(path) in processed_paths: - continue - custom_component_dict = build_langchain_custom_component_list_from_path( - str(path) - ) - custom_component_dicts.append(custom_component_dict) - processed_paths.append(str(path)) - - logger.info(f"Loading {len(custom_component_dicts)} category(ies)") - for custom_component_dict in custom_component_dicts: - # custom_component_dict is a dict of dicts - if not custom_component_dict: - continue - category = list(custom_component_dict.keys())[0] - logger.info( - f"Loading {len(custom_component_dict[category])} component(s) from category {category}" - ) - custom_components_from_file = merge_nested_dicts_with_renaming( - custom_components_from_file, custom_component_dict - ) - - return merge_nested_dicts_with_renaming( - native_components, custom_components_from_file - ) + try: + return get_all_types_dict(settings_service) + except Exception as exc: + raise HTTPException(status_code=500, detail=str(exc)) from exc # For backwards compatibility we will keep the old endpoint From 73e78b6b682df434d4a8048cd5c02a885e8c96bc Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 14:03:15 -0300 Subject: [PATCH 165/213] =?UTF-8?q?=F0=9F=94=80=20merge(listing.py):=20mer?= =?UTF-8?q?ge=20changes=20from=20types.py=20to=20improve=20code=20organiza?= =?UTF-8?q?tion=20and=20readability=20=F0=9F=94=80=20merge(types.py):=20me?= =?UTF-8?q?rge=20changes=20from=20listing.py=20to=20consolidate=20all=20ty?= =?UTF-8?q?pe=20dictionaries=20into=20a=20single=20function=20for=20better?= =?UTF-8?q?=20maintainability=20and=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/listing.py | 39 ++----------- src/backend/langflow/interface/types.py | 68 ++++++++++++++++++++++- 2 files changed, 72 insertions(+), 35 deletions(-) diff --git a/src/backend/langflow/interface/listing.py b/src/backend/langflow/interface/listing.py index 1cab1efbc..2851e4ea8 100644 --- a/src/backend/langflow/interface/listing.py +++ b/src/backend/langflow/interface/listing.py @@ -1,19 +1,4 @@ -from langflow.interface.agents.base import agent_creator -from langflow.interface.chains.base import chain_creator -from langflow.interface.document_loaders.base import documentloader_creator -from langflow.interface.embeddings.base import embedding_creator -from langflow.interface.llms.base import llm_creator -from langflow.interface.memories.base import memory_creator -from langflow.interface.prompts.base import prompt_creator -from langflow.interface.text_splitters.base import textsplitter_creator -from langflow.interface.toolkits.base import toolkits_creator -from langflow.interface.tools.base import tool_creator -from langflow.interface.utilities.base import utility_creator -from langflow.interface.vector_store.base import vectorstore_creator -from langflow.interface.wrappers.base import wrapper_creator -from langflow.interface.output_parsers.base import output_parser_creator -from langflow.interface.retrievers.base import retriever_creator -from langflow.interface.custom.base import custom_component_creator +from langflow.services.utils import get_settings_service from langflow.utils.lazy_load import LazyLoadDictBase @@ -33,24 +18,10 @@ class AllTypesDict(LazyLoadDictBase): } def get_type_dict(self): - return { - "agents": agent_creator.to_list(), - "prompts": prompt_creator.to_list(), - "llms": llm_creator.to_list(), - "tools": tool_creator.to_list(), - "chains": chain_creator.to_list(), - "memory": memory_creator.to_list(), - "toolkits": toolkits_creator.to_list(), - "wrappers": wrapper_creator.to_list(), - "documentLoaders": documentloader_creator.to_list(), - "vectorStore": vectorstore_creator.to_list(), - "embeddings": embedding_creator.to_list(), - "textSplitters": textsplitter_creator.to_list(), - "utilities": utility_creator.to_list(), - "outputParsers": output_parser_creator.to_list(), - "retrievers": retriever_creator.to_list(), - "custom_components": custom_component_creator.to_list(), - } + from langflow.interface.types import get_all_types_dict + + settings_service = get_settings_service() + return get_all_types_dict(settings_service=settings_service) lazy_load_dict = AllTypesDict() diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index fa9967649..25d3e8c8b 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -1,7 +1,7 @@ import ast import contextlib from typing import Any, List -from langflow.api.utils import merge_nested_dicts_with_renaming +from langflow.api.utils import get_new_key from langflow.interface.agents.base import agent_creator from langflow.interface.chains.base import chain_creator from langflow.interface.custom.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES @@ -433,6 +433,24 @@ def build_invalid_menu(invalid_components): return invalid_menu +def merge_nested_dicts_with_renaming(dict1, dict2): + for key, value in dict2.items(): + if ( + key in dict1 + and isinstance(value, dict) + and isinstance(dict1.get(key), dict) + ): + for sub_key, sub_value in value.items(): + if sub_key in dict1[key]: + new_key = get_new_key(dict1[key], sub_key) + dict1[key][new_key] = sub_value + else: + dict1[key][sub_key] = sub_value + else: + dict1[key] = value + return dict1 + + def build_langchain_custom_component_list_from_path(path: str): """Build a list of custom components for the langchain from a given path""" file_list = load_files_from_path(path) @@ -446,3 +464,51 @@ def build_langchain_custom_component_list_from_path(path: str): invalid_menu = build_invalid_menu(invalid_components) return merge_nested_dicts_with_renaming(valid_menu, invalid_menu) + + +def get_all_types_dict(settings_service): + native_components = build_langchain_types_dict() + # custom_components is a list of dicts + # need to merge all the keys into one dict + custom_components_from_file: dict[str, Any] = {} + if settings_service.settings.COMPONENTS_PATH: + logger.info( + f"Building custom components from {settings_service.settings.COMPONENTS_PATH}" + ) + + custom_component_dicts = [] + processed_paths = [] + for path in settings_service.settings.COMPONENTS_PATH: + if str(path) in processed_paths: + continue + custom_component_dict = build_langchain_custom_component_list_from_path( + str(path) + ) + custom_component_dicts.append(custom_component_dict) + processed_paths.append(str(path)) + + logger.info(f"Loading {len(custom_component_dicts)} category(ies)") + for custom_component_dict in custom_component_dicts: + # custom_component_dict is a dict of dicts + if not custom_component_dict: + continue + category = list(custom_component_dict.keys())[0] + logger.info( + f"Loading {len(custom_component_dict[category])} component(s) from category {category}" + ) + custom_components_from_file = merge_nested_dicts_with_renaming( + custom_components_from_file, custom_component_dict + ) + + return merge_nested_dicts_with_renaming( + native_components, custom_components_from_file + ) + + +def merge_nested_dicts(dict1, dict2): + for key, value in dict2.items(): + if isinstance(value, dict) and isinstance(dict1.get(key), dict): + dict1[key] = merge_nested_dicts(dict1[key], value) + else: + dict1[key] = value + return dict1 From 05b1f3658ee58430c21fc1322b1be99aee1140f5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 14:05:46 -0300 Subject: [PATCH 166/213] =?UTF-8?q?=F0=9F=94=A7=20chore(frontend):=20add?= =?UTF-8?q?=20newline=20at=20the=20end=20of=20package-lock.json=20file=20t?= =?UTF-8?q?o=20adhere=20to=20coding=20conventions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index cd190db12..c4a0660cb 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -10978,4 +10978,4 @@ } } } -} \ No newline at end of file +} From a658f5af89ed69a643c82528e0ffbf99ed443eab Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 14:15:55 -0300 Subject: [PATCH 167/213] =?UTF-8?q?=F0=9F=94=A7=20chore(conftest.py):=20co?= =?UTF-8?q?mment=20out=20unused=20monkeypatching=20of=20USE=5FCELERY=20var?= =?UTF-8?q?iable=20to=20improve=20code=20readability=20and=20maintainabili?= =?UTF-8?q?ty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0109845af..b767b602d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -106,7 +106,7 @@ def distributed_client_fixture(session: Session, monkeypatch, distributed_env): # monkeypatch langflow.services.task.manager.USE_CELERY to True monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", False) - monkeypatch.setattr(manager, "USE_CELERY", True) + # monkeypatch.setattr(manager, "USE_CELERY", True) monkeypatch.setattr( celery_app, "celery_app", celery_app.make_celery("langflow", Config) ) From 1c0f18f89751bad31f34689f02679d7ac7305684 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 14:56:16 -0300 Subject: [PATCH 168/213] =?UTF-8?q?=F0=9F=94=A7=20fix(=5F=5Fmain=5F=5F.py)?= =?UTF-8?q?:=20update=20import=20statements=20to=20reflect=20changes=20in?= =?UTF-8?q?=20module=20structure=20=F0=9F=94=A7=20fix(endpoints.py):=20upd?= =?UTF-8?q?ate=20import=20statement=20to=20reflect=20changes=20in=20module?= =?UTF-8?q?=20structure=20=F0=9F=94=A7=20fix(flows.py):=20update=20import?= =?UTF-8?q?=20statements=20to=20reflect=20changes=20in=20module=20structur?= =?UTF-8?q?e=20=F0=9F=94=A7=20fix(login.py):=20update=20import=20statement?= =?UTF-8?q?=20to=20reflect=20changes=20in=20module=20structure=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(base.py):=20update=20import=20statement=20to?= =?UTF-8?q?=20reflect=20changes=20in=20module=20structure=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(base.py):=20update=20import=20statement=20to=20reflect?= =?UTF-8?q?=20changes=20in=20module=20structure=20=F0=9F=94=A7=20fix(custo?= =?UTF-8?q?m=5Fcomponent.py):=20update=20import=20statement=20to=20reflect?= =?UTF-8?q?=20changes=20in=20module=20structure=20=F0=9F=94=A7=20fix(base.?= =?UTF-8?q?py):=20update=20import=20statement=20to=20reflect=20changes=20i?= =?UTF-8?q?n=20module=20structure=20=F0=9F=94=A7=20fix(base.py):=20update?= =?UTF-8?q?=20import=20statement=20to=20reflect=20changes=20in=20module=20?= =?UTF-8?q?structure=20=F0=9F=94=A7=20fix(base.py):=20update=20import=20st?= =?UTF-8?q?atement=20to=20reflect=20changes=20in=20module=20structure=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(base.py):=20update=20import=20statement=20to?= =?UTF-8?q?=20reflect=20changes=20in=20module=20structure=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(base.py):=20update=20import=20statement=20to=20reflect?= =?UTF-8?q?=20changes=20in=20module=20structure=20=F0=9F=94=A7=20fix(base.?= =?UTF-8?q?py):=20update=20import=20statement=20to=20reflect=20changes=20i?= =?UTF-8?q?n=20module=20structure=20=F0=9F=94=A7=20fix(base.py):=20update?= =?UTF-8?q?=20import=20statement=20to=20reflect=20changes=20in=20module=20?= =?UTF-8?q?structure=20=F0=9F=94=A7=20fix(base.py):=20update=20import=20st?= =?UTF-8?q?atement=20to=20reflect=20changes=20in=20module=20structure=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(base.py):=20update=20import=20statement=20to?= =?UTF-8?q?=20reflect=20changes=20in=20module=20structure=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(base.py):=20update=20import=20statement=20to=20reflect?= =?UTF-8?q?=20changes=20in=20module=20structure=20=F0=9F=94=A7=20fix(base.?= =?UTF-8?q?py):=20update=20import=20statement=20to=20reflect=20changes=20i?= =?UTF-8?q?n=20module=20structure=20=F0=9F=94=A7=20fix(base.py):=20update?= =?UTF-8?q?=20import=20statement=20to=20reflect=20changes=20in=20module=20?= =?UTF-8?q?structure=20=F0=9F=94=A7=20fix(base.py):=20update=20import=20st?= =?UTF-8?q?atement=20to=20reflect=20changes=20in=20module=20structure=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(base.py):=20update=20import=20statement=20to?= =?UTF-8?q?=20reflect=20changes=20in=20module=20structure=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(base.py):=20update=20import=20statement=20to=20reflect?= =?UTF-8?q?=20changes=20in=20module=20structure=20=F0=9F=94=A7=20fix(base.?= =?UTF-8?q?py):=20update=20import=20statement=20to=20reflect=20changes=20i?= =?UTF-8?q?n=20module=20structure=20=F0=9F=94=A7=20fix(base.py):=20update?= =?UTF-8?q?=20import=20statement=20to=20reflect=20changes=20in=20module=20?= =?UTF-8?q?structure=20=F0=9F=94=A7=20fix(base.py):=20update=20import=20st?= =?UTF-8?q?atement=20to=20reflect=20changes=20in=20module=20structure=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(base.py):=20update=20import=20statement=20to?= =?UTF-8?q?=20reflect=20changes=20in=20module=20structure=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(base.py):=20update=20import=20statement=20to=20reflect?= =?UTF-8?q?=20changes=20in=20module=20structure=20=F0=9F=94=A7=20fix(base.?= =?UTF-8?q?py):=20update=20import=20statement=20to=20reflect=20changes=20i?= =?UTF-8?q?n=20module=20structure=20=F0=9F=94=A7=20fix(base.py):=20update?= =?UTF-8?q?=20import=20statement=20to=20reflect=20changes=20in=20module=20?= =?UTF-8?q?structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 fix(process.py): update import statement for get_session_service in langflow.processing.process module to reflect correct module location 🔧 fix(utils.py): update import statement for get_session and get_settings_service in langflow.services.auth.utils module to reflect correct module location 🔧 fix(manager.py): update import statement for ServiceType.CACHE_MANAGER in langflow.services.chat.manager module to reflect correct module location 🔧 fix(manager.py): update import statement for ServiceType.DATABASE_MANAGER in langflow.services.chat.manager module to reflect correct module location 🔧 fix(utils.py): update import statement for get_settings_service in langflow.services.database.manager module to reflect correct module location 🔧 fix(utils.py): update import statement for get_settings_service in langflow.services.database.utils module to reflect correct module location 🔧 fix(getters.py): update import statement for DatabaseManager, SettingsManager, ChatManager in langflow.services.getters module to reflect correct module location 🔧 fix(getters.py): update import statement for get_settings_manager in langflow.services.getters module to reflect correct function name change 🔧 fix(getters.py): update import statement for get_db_manager in langflow.services.getters module to reflect correct function name change 🔧 fix(getters.py): update import statement for get_chat_manager in langflow.services.getters module to reflect correct function name change 🔧 fix(getters.py): update import statement for get_settings_manager in langflow.services.getters module to reflect correct function name change 🔧 fix(getters.py): update import statement for get_db_manager in langflow.services.getters module to reflect correct function name change 🔧 fix(getters.py): update import statement for get_session in langflow.services.getters module to reflect correct function name change 🔧 fix(getters.py): update import statement for get_chat_manager in langflow.services.getters module to reflect correct function name change 🔧 fix(langfuse.py): update import statement for get_settings_service in langflow.services.plugins.langfuse module to reflect correct module location 🔧 fix(schema.py): update import statement for ServiceType.AUTH_MANAGER, ServiceType.CACHE_MANAGER, ServiceType.SETTINGS_MANAGER, ServiceType.DATABASE_MANAGER, ServiceType.CHAT_MANAGER, ServiceType.SESSION_MANAGER, ServiceType.TASK_MANAGER in langflow.services.schema module to reflect correct module location 🔧 fix --- src/backend/langflow/__main__.py | 2 +- src/backend/langflow/api/v1/endpoints.py | 2 +- src/backend/langflow/api/v1/flows.py | 4 +- src/backend/langflow/api/v1/login.py | 2 +- src/backend/langflow/interface/agents/base.py | 2 +- src/backend/langflow/interface/base.py | 2 +- src/backend/langflow/interface/chains/base.py | 2 +- .../interface/custom/custom_component.py | 2 +- .../interface/document_loaders/base.py | 2 +- .../langflow/interface/embeddings/base.py | 2 +- src/backend/langflow/interface/listing.py | 2 +- src/backend/langflow/interface/llms/base.py | 2 +- .../langflow/interface/memories/base.py | 2 +- .../langflow/interface/output_parsers/base.py | 2 +- .../langflow/interface/prompts/base.py | 2 +- .../langflow/interface/retrievers/base.py | 2 +- .../langflow/interface/text_splitters/base.py | 2 +- .../langflow/interface/toolkits/base.py | 2 +- src/backend/langflow/interface/tools/base.py | 2 +- .../langflow/interface/utilities/base.py | 2 +- src/backend/langflow/interface/utils.py | 2 +- .../langflow/interface/vector_store/base.py | 2 +- src/backend/langflow/processing/process.py | 2 +- src/backend/langflow/services/auth/utils.py | 2 +- src/backend/langflow/services/chat/manager.py | 2 +- .../langflow/services/database/manager.py | 2 +- .../langflow/services/database/utils.py | 2 +- src/backend/langflow/services/getters.py | 57 ++++++++++++++++--- .../langflow/services/plugins/langfuse.py | 2 +- src/backend/langflow/services/schema.py | 14 ++--- src/backend/langflow/worker.py | 2 +- tests/test_process.py | 2 +- 32 files changed, 86 insertions(+), 47 deletions(-) diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index c63b371ed..c17d39d91 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -3,7 +3,7 @@ import time import httpx from langflow.services.database.utils import session_getter from langflow.services.manager import initialize_services, initialize_settings_service -from langflow.services.utils import get_db_service, get_settings_service +from langflow.services.getters import get_db_service, get_settings_service from multiprocess import Process, cpu_count # type: ignore import platform diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 4d034a59f..ade2f19e7 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -26,7 +26,7 @@ from langflow.interface.types import ( build_langchain_template_custom_component, ) -from langflow.services.utils import get_session +from langflow.services.getters import get_session try: from langflow.worker import process_graph_cached_task diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index be49ad836..9953db0db 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -12,8 +12,8 @@ from langflow.services.database.models.flow import ( FlowUpdate, ) from langflow.services.database.models.user.user import User -from langflow.services.utils import get_session -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_session +from langflow.services.getters import get_settings_service import orjson from sqlmodel import Session from fastapi import APIRouter, Depends, HTTPException diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index 969cf096e..d45302c2e 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -12,7 +12,7 @@ from langflow.services.auth.utils import ( get_current_active_user, ) -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service router = APIRouter(tags=["Login"]) diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index 16df6d98f..696f5afd0 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -5,7 +5,7 @@ from langchain.agents import types from langflow.custom.customs import get_custom_nodes from langflow.interface.agents.custom import CUSTOM_AGENTS from langflow.interface.base import LangChainTypeCreator -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.agents import AgentFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/base.py b/src/backend/langflow/interface/base.py index f622cf478..13dd05619 100644 --- a/src/backend/langflow/interface/base.py +++ b/src/backend/langflow/interface/base.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Type, Union from langchain.chains.base import Chain from langchain.agents import AgentExecutor -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from pydantic import BaseModel from langflow.template.field.base import TemplateField diff --git a/src/backend/langflow/interface/chains/base.py b/src/backend/langflow/interface/chains/base.py index c18ff34ab..b2d07b7e4 100644 --- a/src/backend/langflow/interface/chains/base.py +++ b/src/backend/langflow/interface/chains/base.py @@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Type from langflow.custom.customs import get_custom_nodes from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.chains import ChainFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/custom/custom_component.py b/src/backend/langflow/interface/custom/custom_component.py index ef6761081..49ac81ab1 100644 --- a/src/backend/langflow/interface/custom/custom_component.py +++ b/src/backend/langflow/interface/custom/custom_component.py @@ -4,7 +4,7 @@ from fastapi import HTTPException from langflow.interface.custom.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES from langflow.interface.custom.component import Component from langflow.interface.custom.directory_reader import DirectoryReader -from langflow.services.utils import get_db_service +from langflow.services.getters import get_db_service from langflow.interface.custom.utils import extract_inner_type from langflow.utils import validate diff --git a/src/backend/langflow/interface/document_loaders/base.py b/src/backend/langflow/interface/document_loaders/base.py index 84c84ee55..e2c379b67 100644 --- a/src/backend/langflow/interface/document_loaders/base.py +++ b/src/backend/langflow/interface/document_loaders/base.py @@ -1,7 +1,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode from langflow.interface.custom_lists import documentloaders_type_to_cls_dict diff --git a/src/backend/langflow/interface/embeddings/base.py b/src/backend/langflow/interface/embeddings/base.py index 72d9a4cdb..d280cf1c1 100644 --- a/src/backend/langflow/interface/embeddings/base.py +++ b/src/backend/langflow/interface/embeddings/base.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import embedding_type_to_cls_dict -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode diff --git a/src/backend/langflow/interface/listing.py b/src/backend/langflow/interface/listing.py index 2851e4ea8..aa72e568e 100644 --- a/src/backend/langflow/interface/listing.py +++ b/src/backend/langflow/interface/listing.py @@ -1,4 +1,4 @@ -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.utils.lazy_load import LazyLoadDictBase diff --git a/src/backend/langflow/interface/llms/base.py b/src/backend/langflow/interface/llms/base.py index 74ec78af6..ffb7fa2f2 100644 --- a/src/backend/langflow/interface/llms/base.py +++ b/src/backend/langflow/interface/llms/base.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import llm_type_to_cls_dict -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.llms import LLMFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/memories/base.py b/src/backend/langflow/interface/memories/base.py index fa3576305..3f3658304 100644 --- a/src/backend/langflow/interface/memories/base.py +++ b/src/backend/langflow/interface/memories/base.py @@ -2,7 +2,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import memory_type_to_cls_dict -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.memories import MemoryFrontendNode diff --git a/src/backend/langflow/interface/output_parsers/base.py b/src/backend/langflow/interface/output_parsers/base.py index 93d4eeeda..06dfdf4eb 100644 --- a/src/backend/langflow/interface/output_parsers/base.py +++ b/src/backend/langflow/interface/output_parsers/base.py @@ -4,7 +4,7 @@ from langchain import output_parsers from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/prompts/base.py b/src/backend/langflow/interface/prompts/base.py index 01ac40ab6..29d3e8ba8 100644 --- a/src/backend/langflow/interface/prompts/base.py +++ b/src/backend/langflow/interface/prompts/base.py @@ -5,7 +5,7 @@ from langchain import prompts from langflow.custom.customs import get_custom_nodes from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.prompts import PromptFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/retrievers/base.py b/src/backend/langflow/interface/retrievers/base.py index 5562cf264..4ee40e659 100644 --- a/src/backend/langflow/interface/retrievers/base.py +++ b/src/backend/langflow/interface/retrievers/base.py @@ -4,7 +4,7 @@ from langchain import retrievers from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.retrievers import RetrieverFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/text_splitters/base.py b/src/backend/langflow/interface/text_splitters/base.py index abf3640c8..4f337817f 100644 --- a/src/backend/langflow/interface/text_splitters/base.py +++ b/src/backend/langflow/interface/text_splitters/base.py @@ -1,7 +1,7 @@ from typing import Dict, List, Optional, Type from langflow.interface.base import LangChainTypeCreator -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode from langflow.interface.custom_lists import textsplitter_type_to_cls_dict diff --git a/src/backend/langflow/interface/toolkits/base.py b/src/backend/langflow/interface/toolkits/base.py index d358fcde8..73ca4852f 100644 --- a/src/backend/langflow/interface/toolkits/base.py +++ b/src/backend/langflow/interface/toolkits/base.py @@ -4,7 +4,7 @@ from langchain.agents import agent_toolkits from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class, import_module -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from loguru import logger from langflow.utils.util import build_template_from_class diff --git a/src/backend/langflow/interface/tools/base.py b/src/backend/langflow/interface/tools/base.py index cdc161d8f..a99025ff7 100644 --- a/src/backend/langflow/interface/tools/base.py +++ b/src/backend/langflow/interface/tools/base.py @@ -15,7 +15,7 @@ from langflow.interface.tools.constants import ( OTHER_TOOLS, ) from langflow.interface.tools.util import get_tool_params -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.field.base import TemplateField from langflow.template.template.base import Template diff --git a/src/backend/langflow/interface/utilities/base.py b/src/backend/langflow/interface/utilities/base.py index b1e7c461b..665143da2 100644 --- a/src/backend/langflow/interface/utilities/base.py +++ b/src/backend/langflow/interface/utilities/base.py @@ -5,7 +5,7 @@ from langchain import SQLDatabase, utilities from langflow.custom.customs import get_custom_nodes from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.utilities import UtilitiesFrontendNode from loguru import logger diff --git a/src/backend/langflow/interface/utils.py b/src/backend/langflow/interface/utils.py index 388697b2a..7b78e75c5 100644 --- a/src/backend/langflow/interface/utils.py +++ b/src/backend/langflow/interface/utils.py @@ -10,7 +10,7 @@ from langchain.base_language import BaseLanguageModel from PIL.Image import Image from loguru import logger from langflow.services.chat.config import ChatConfig -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service def load_file_into_dict(file_path: str) -> dict: diff --git a/src/backend/langflow/interface/vector_store/base.py b/src/backend/langflow/interface/vector_store/base.py index 7c0567362..786031a6b 100644 --- a/src/backend/langflow/interface/vector_store/base.py +++ b/src/backend/langflow/interface/vector_store/base.py @@ -4,7 +4,7 @@ from langchain import vectorstores from langflow.interface.base import LangChainTypeCreator from langflow.interface.importing.utils import import_class -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode from loguru import logger diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index fa35bd348..4c96e2fde 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -6,7 +6,7 @@ from langflow.interface.run import ( get_memory_key, update_memory_keys, ) -from langflow.services.utils import get_session_service +from langflow.services.getters import get_session_service from loguru import logger from langflow.graph import Graph from langchain.chains.base import Chain diff --git a/src/backend/langflow/services/auth/utils.py b/src/backend/langflow/services/auth/utils.py index a8c7d7b4f..b72514798 100644 --- a/src/backend/langflow/services/auth/utils.py +++ b/src/backend/langflow/services/auth/utils.py @@ -12,7 +12,7 @@ from langflow.services.database.models.user.crud import ( get_user_by_username, update_user_last_login_at, ) -from langflow.services.utils import get_session, get_settings_service +from langflow.services.getters import get_session, get_settings_service from sqlmodel import Session oauth2_login = OAuth2PasswordBearer(tokenUrl="api/v1/login") diff --git a/src/backend/langflow/services/chat/manager.py b/src/backend/langflow/services/chat/manager.py index 029c26ecd..3fe937ce8 100644 --- a/src/backend/langflow/services/chat/manager.py +++ b/src/backend/langflow/services/chat/manager.py @@ -52,7 +52,7 @@ class ChatService(Service): self.chat_history = ChatHistory() self.chat_cache = cache_service self.chat_cache.attach(self.update) - self.cache_service = service_manager.get(ServiceType.CACHE_MANAGER) + self.cache_service = service_manager.get(ServiceType.CACHE_SERVICE) def on_chat_history_update(self): """Send the last chat message to the client.""" diff --git a/src/backend/langflow/services/database/manager.py b/src/backend/langflow/services/database/manager.py index 46ced4524..ca7f34d10 100644 --- a/src/backend/langflow/services/database/manager.py +++ b/src/backend/langflow/services/database/manager.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING from langflow.services.base import Service from langflow.services.database.models.user.crud import get_user_by_username from langflow.services.database.utils import Result, TableResults -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from sqlalchemy import inspect import sqlalchemy as sa from sqlmodel import SQLModel, Session, create_engine diff --git a/src/backend/langflow/services/database/utils.py b/src/backend/langflow/services/database/utils.py index 5ac62ee93..ce4990fa1 100644 --- a/src/backend/langflow/services/database/utils.py +++ b/src/backend/langflow/services/database/utils.py @@ -13,7 +13,7 @@ def initialize_database(): logger.debug("Initializing database") from langflow.services import service_manager, ServiceType - database_service = service_manager.get(ServiceType.DATABASE_MANAGER) + database_service = service_manager.get(ServiceType.DATABASE_SERVICE) try: database_service.check_schema_health() except Exception as exc: diff --git a/src/backend/langflow/services/getters.py b/src/backend/langflow/services/getters.py index 8b32aef02..43b344d40 100644 --- a/src/backend/langflow/services/getters.py +++ b/src/backend/langflow/services/getters.py @@ -3,24 +3,63 @@ from typing import TYPE_CHECKING, Generator if TYPE_CHECKING: - from langflow.services.database.manager import DatabaseManager - from langflow.services.settings.manager import SettingsManager - from langflow.services.chat.manager import ChatManager + from langflow.services.database.manager import DatabaseService + from langflow.services.settings.manager import SettingsService + from langflow.services.cache.manager import BaseCacheService + from langflow.services.session.manager import SessionService + from langflow.services.task.manager import TaskService + from langflow.services.chat.manager import ChatService from sqlmodel import Session -def get_settings_manager() -> "SettingsManager": - return service_manager.get(ServiceType.SETTINGS_MANAGER) +def get_settings_service() -> "SettingsService": + try: + return service_manager.get(ServiceType.SETTINGS_MANAGER) + except ValueError: + # initialize settings service + from langflow.services.manager import initialize_settings_service + + initialize_settings_service() + return service_manager.get(ServiceType.SETTINGS_MANAGER) -def get_db_manager() -> "DatabaseManager": +def get_db_service() -> "DatabaseService": return service_manager.get(ServiceType.DATABASE_MANAGER) def get_session() -> Generator["Session", None, None]: - db_manager = service_manager.get(ServiceType.DATABASE_MANAGER) - yield from db_manager.get_session() + db_service = service_manager.get(ServiceType.DATABASE_MANAGER) + yield from db_service.get_session() -def get_chat_manager() -> "ChatManager": +def get_cache_service() -> "BaseCacheService": + return service_manager.get(ServiceType.CACHE_MANAGER) + + +def get_session_service() -> "SessionService": + return service_manager.get(ServiceType.SESSION_MANAGER) + + +def get_task_service() -> "TaskService": + return service_manager.get(ServiceType.TASK_MANAGER) + + +def get_chat_service() -> "ChatService": return service_manager.get(ServiceType.CHAT_MANAGER) + + +def get_settings_service() -> "SettingsService": + return service_manager.get(ServiceType.SETTINGS_SERVICE) + + +def get_db_service() -> "DatabaseService": + return service_manager.get(ServiceType.DATABASE_SERVICE) + + +def get_session() -> Generator["Session", None, None]: + db_service = service_manager.get(ServiceType.DATABASE_SERVICE) + yield from db_service.get_session() + + +def get_chat_service() -> "ChatService": + return service_manager.get(ServiceType.CHAT_SERVICE) diff --git a/src/backend/langflow/services/plugins/langfuse.py b/src/backend/langflow/services/plugins/langfuse.py index bc50ccc2c..29d59808b 100644 --- a/src/backend/langflow/services/plugins/langfuse.py +++ b/src/backend/langflow/services/plugins/langfuse.py @@ -1,4 +1,4 @@ -from langflow.services.utils import get_settings_service +from langflow.services.getters import get_settings_service from langflow.utils.logger import logger ### Temporary implementation diff --git a/src/backend/langflow/services/schema.py b/src/backend/langflow/services/schema.py index 8b2bb75c2..8b3b41fcb 100644 --- a/src/backend/langflow/services/schema.py +++ b/src/backend/langflow/services/schema.py @@ -7,10 +7,10 @@ class ServiceType(str, Enum): registered with the service manager. """ - AUTH_MANAGER = "auth_service" - CACHE_MANAGER = "cache_service" - SETTINGS_MANAGER = "settings_service" - DATABASE_MANAGER = "database_service" - CHAT_MANAGER = "chat_service" - SESSION_MANAGER = "session_service" - TASK_MANAGER = "task_service" + AUTH_SERVICE = "auth_service" + CACHE_SERVICE = "cache_service" + SETTINGS_SERVICE = "settings_service" + DATABASE_SERVICE = "database_service" + CHAT_SERVICE = "chat_service" + SESSION_SERVICE = "session_service" + TASK_SERVICE = "task_service" diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 92f6f1f7c..7b5f67b7b 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -10,7 +10,7 @@ from langflow.processing.process import ( process_inputs, ) from langflow.services.manager import initialize_session_service -from langflow.services.utils import get_session_service +from langflow.services.getters import get_session_service if TYPE_CHECKING: from langflow.graph.vertex.base import Vertex diff --git a/tests/test_process.py b/tests/test_process.py index 775d17145..0588800dc 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -1,5 +1,5 @@ from langflow.processing.process import process_tweaks -from langflow.services.utils import get_session_service +from langflow.services.getters import get_session_service def test_no_tweaks(): From 479a80863448b1f9ae7d747d67cf0451c2485a8b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 18:07:55 -0300 Subject: [PATCH 169/213] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20rem?= =?UTF-8?q?ove=20unused=20import=20and=20function=20call=20to=20improve=20?= =?UTF-8?q?code=20cleanliness=20and=20maintainability=20=F0=9F=94=A7=20fix?= =?UTF-8?q?(endpoints.py):=20move=20import=20statement=20to=20the=20top=20?= =?UTF-8?q?of=20the=20file=20for=20better=20organization=20and=20readabili?= =?UTF-8?q?ty=20=F0=9F=94=A7=20fix(getters.py):=20change=20service=20type?= =?UTF-8?q?=20from=20DATABASE=5FMANAGER=20to=20DATABASE=5FSERVICE=20for=20?= =?UTF-8?q?consistency=20and=20clarity=20=F0=9F=94=A7=20fix(getters.py):?= =?UTF-8?q?=20change=20service=20type=20from=20CACHE=5FMANAGER=20to=20CACH?= =?UTF-8?q?E=5FSERVICE=20for=20consistency=20and=20clarity=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(getters.py):=20change=20service=20type=20from=20SESSION?= =?UTF-8?q?=5FMANAGER=20to=20SESSION=5FSERVICE=20for=20consistency=20and?= =?UTF-8?q?=20clarity=20=F0=9F=94=A7=20fix(getters.py):=20change=20service?= =?UTF-8?q?=20type=20from=20TASK=5FMANAGER=20to=20TASK=5FSERVICE=20for=20c?= =?UTF-8?q?onsistency=20and=20clarity=20=F0=9F=94=A7=20fix(getters.py):=20?= =?UTF-8?q?remove=20unused=20function=20get=5Fchat=5Fservice()=20to=20impr?= =?UTF-8?q?ove=20code=20cleanliness=20and=20maintainability=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(getters.py):=20remove=20duplicate=20function=20get=5Fset?= =?UTF-8?q?tings=5Fservice()=20to=20improve=20code=20cleanliness=20and=20m?= =?UTF-8?q?aintainability=20=F0=9F=94=A7=20fix(getters.py):=20remove=20dup?= =?UTF-8?q?licate=20function=20get=5Fdb=5Fservice()=20to=20improve=20code?= =?UTF-8?q?=20cleanliness=20and=20maintainability=20=F0=9F=94=A7=20fix(get?= =?UTF-8?q?ters.py):=20remove=20duplicate=20function=20get=5Fsession()=20t?= =?UTF-8?q?o=20improve=20code=20cleanliness=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(utils.py):=20remove=20unused=20import=20stat?= =?UTF-8?q?ement=20to=20improve=20code=20cleanliness=20and=20maintainabili?= =?UTF-8?q?ty=20=F0=9F=94=A7=20fix(utils.py):=20remove=20unused=20function?= =?UTF-8?q?=20setup=5Fsuperuser()=20to=20improve=20code=20cleanliness=20an?= =?UTF-8?q?d=20maintainability=20=F0=9F=94=A7=20fix(utils.py):=20remove=20?= =?UTF-8?q?unused=20function=20teardown=5Fsuperuser()=20to=20improve=20cod?= =?UTF-8?q?e=20cleanliness=20and=20maintainability=20=F0=9F=94=A7=20fix(ut?= =?UTF-8?q?ils.py):=20remove=20unused=20function=20teardown=5Fservices()?= =?UTF-8?q?=20to=20improve=20code=20cleanliness=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(utils.py):=20remove=20unused=20function=20in?= =?UTF-8?q?itialize=5Fsettings=5Fmanager()=20to=20improve=20code=20cleanli?= =?UTF-8?q?ness=20and=20maintainability=20=F0=9F=94=A7=20fix(utils.py):=20?= =?UTF-8?q?remove=20unused=20function=20initialize=5Fsession=5Fmanager()?= =?UTF-8?q?=20to=20improve=20code=20cleanliness=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(utils.py):=20remove=20unused=20function=20in?= =?UTF-8?q?itialize=5Fservices()=20to=20improve=20code=20cleanliness=20and?= =?UTF-8?q?=20maintainability=20=F0=9F=94=A7=20fix(conftest.py):=20remove?= =?UTF-8?q?=20unused=20import=20statement=20to=20improve=20code=20cleanlin?= =?UTF-8?q?ess=20and=20maintainability=20=F0=9F=94=A7=20fix(conftest.py):?= =?UTF-8?q?=20remove=20unused=20function=20get=5Fsession=5Foverride()=20to?= =?UTF-8?q?=20improve=20code=20cleanliness=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(conftest.py):=20remove=20unused=20function?= =?UTF-8?q?=20distributed=5Fclient=5Ffixture()=20to=20improve=20code=20cle?= =?UTF-8?q?anliness=20and=20maintainability=20=F0=9F=94=A7=20fix(conftest.?= =?UTF-8?q?py):=20remove=20unused=20function=20client=5Ffixture()=20to=20i?= =?UTF-8?q?mprove=20code=20cleanliness=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(conftest.py):=20remove=20unused=20function?= =?UTF-8?q?=20test=5Fuser()=20to=20improve=20code=20cleanliness=20and=20ma?= =?UTF-8?q?intainability=20=F0=9F=94=A7=20fix(conftest.py):=20remove=20unu?= =?UTF-8?q?sed=20function=20active=5Fuser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(test_endpoints.py): update import statements to use get_db_service instead of get_db_manager to improve code semantics 🐛 fix(test_login.py): update import statements to use get_db_service instead of get_db_manager to improve code semantics 🐛 fix(test_setup_superuser.py): update import statements to use get_db_service instead of get_db_manager to improve code semantics 🐛 fix(test_user.py): update import statements to use get_db_service instead of get_db_manager to improve code semantics --- src/backend/langflow/api/v1/endpoints.py | 8 +- src/backend/langflow/services/getters.py | 45 ++++------- src/backend/langflow/services/utils.py | 75 ++++++++++-------- tests/conftest.py | 33 +++++--- tests/test_database.py | 4 +- tests/test_endpoints.py | 33 +++++++- tests/test_login.py | 4 +- tests/test_setup_superuser.py | 97 +++++++++++++----------- tests/test_user.py | 6 +- 9 files changed, 171 insertions(+), 134 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index ade2f19e7..b08715254 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -22,10 +22,6 @@ from langflow.api.v1.schemas import ( ) -from langflow.interface.types import ( - build_langchain_template_custom_component, -) - from langflow.services.getters import get_session try: @@ -207,6 +203,10 @@ def get_version(): async def custom_component( raw_code: CustomComponentCode, ): + from langflow.interface.types import ( + build_langchain_template_custom_component, + ) + extractor = CustomComponent(code=raw_code.code) extractor.is_check_valid() diff --git a/src/backend/langflow/services/getters.py b/src/backend/langflow/services/getters.py index 43b344d40..e88b998b5 100644 --- a/src/backend/langflow/services/getters.py +++ b/src/backend/langflow/services/getters.py @@ -14,42 +14,13 @@ if TYPE_CHECKING: def get_settings_service() -> "SettingsService": try: - return service_manager.get(ServiceType.SETTINGS_MANAGER) + return service_manager.get(ServiceType.SETTINGS_SERVICE) except ValueError: # initialize settings service from langflow.services.manager import initialize_settings_service initialize_settings_service() - return service_manager.get(ServiceType.SETTINGS_MANAGER) - - -def get_db_service() -> "DatabaseService": - return service_manager.get(ServiceType.DATABASE_MANAGER) - - -def get_session() -> Generator["Session", None, None]: - db_service = service_manager.get(ServiceType.DATABASE_MANAGER) - yield from db_service.get_session() - - -def get_cache_service() -> "BaseCacheService": - return service_manager.get(ServiceType.CACHE_MANAGER) - - -def get_session_service() -> "SessionService": - return service_manager.get(ServiceType.SESSION_MANAGER) - - -def get_task_service() -> "TaskService": - return service_manager.get(ServiceType.TASK_MANAGER) - - -def get_chat_service() -> "ChatService": - return service_manager.get(ServiceType.CHAT_MANAGER) - - -def get_settings_service() -> "SettingsService": - return service_manager.get(ServiceType.SETTINGS_SERVICE) + return service_manager.get(ServiceType.SETTINGS_SERVICE) def get_db_service() -> "DatabaseService": @@ -61,5 +32,17 @@ def get_session() -> Generator["Session", None, None]: yield from db_service.get_session() +def get_cache_service() -> "BaseCacheService": + return service_manager.get(ServiceType.CACHE_SERVICE) + + +def get_session_service() -> "SessionService": + return service_manager.get(ServiceType.SESSION_SERVICE) + + +def get_task_service() -> "TaskService": + return service_manager.get(ServiceType.TASK_SERVICE) + + def get_chat_service() -> "ChatService": return service_manager.get(ServiceType.CHAT_SERVICE) diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index 71ad9258d..940ebc068 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -1,3 +1,4 @@ +from langflow.services.auth import service from langflow.services.auth.utils import create_super_user from langflow.services.database.utils import initialize_database from langflow.services.manager import service_manager @@ -10,20 +11,18 @@ from .getters import get_session, get_settings_service from loguru import logger -def setup_superuser(): +def setup_superuser(settings_service, session): """ Setup the superuser. """ # We will use the SUPERUSER and SUPERUSER_PASSWORD # vars on settings_manager.auth_settings to create the superuser # if it does not exist. - settings_manager = get_settings_service() - if settings_manager.auth_settings.AUTO_LOGIN: + if settings_service.auth_settings.AUTO_LOGIN: logger.debug("AUTO_LOGIN is set to True. Creating default superuser.") - session = next(get_session()) - username = settings_manager.auth_settings.SUPERUSER - password = settings_manager.auth_settings.SUPERUSER_PASSWORD + username = settings_service.auth_settings.SUPERUSER + password = settings_service.auth_settings.SUPERUSER_PASSWORD if username == DEFAULT_SUPERUSER and password == DEFAULT_SUPERUSER_PASSWORD: logger.debug("Default superuser credentials detected.") logger.debug("Creating default superuser.") @@ -50,21 +49,20 @@ def setup_superuser(): "Could not create superuser. Please create a superuser manually." ) from exc # reset superuser credentials - settings_manager.auth_settings.reset_credentials() + settings_service.auth_settings.reset_credentials() logger.debug("Superuser created successfully.") -def teardown_superuser(): +def teardown_superuser(settings_service, session): """ Teardown the superuser. """ # If AUTO_LOGIN is True, we will remove the default superuser # from the database. - settings_manager = get_settings_service() - if settings_manager.auth_settings.AUTO_LOGIN: + + if settings_service.auth_settings.AUTO_LOGIN: logger.debug("AUTO_LOGIN is set to True. Removing default superuser.") - session = next(get_session()) - username = settings_manager.auth_settings.SUPERUSER + username = settings_service.auth_settings.SUPERUSER from langflow.services.database.models.user.user import User user = session.query(User).filter(User.username == username).first() @@ -80,35 +78,38 @@ def teardown_services(): """ Teardown all the services. """ - teardown_superuser() - service_manager.teardown() + try: + teardown_superuser(get_settings_service(), next(get_session())) + service_manager.teardown() + except Exception as exc: + logger.exception(exc) -def initialize_settings_manager(): +def initialize_settings_service(): """ Initialize the settings manager. """ from langflow.services.settings import factory as settings_factory - service_manager.register_factory(settings_factory.SettingsManagerFactory()) + service_manager.register_factory(settings_factory.SettingsServiceFactory()) -def initialize_session_manager(): +def initialize_session_service(): """ Initialize the session manager. """ - from langflow.services.session import factory as session_manager_factory # type: ignore + from langflow.services.session import factory as session_service_factory # type: ignore from langflow.services.cache import factory as cache_factory - initialize_settings_manager() + initialize_settings_service() service_manager.register_factory( - cache_factory.CacheManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_SERVICE] ) service_manager.register_factory( - session_manager_factory.SessionManagerFactory(), - dependencies=[ServiceType.CACHE_MANAGER], + session_service_factory.SessionServiceFactory(), + dependencies=[ServiceType.CACHE_SERVICE], ) @@ -121,23 +122,33 @@ def initialize_services(): from langflow.services.chat import factory as chat_factory from langflow.services.settings import factory as settings_factory from langflow.services.auth import factory as auth_factory + from langflow.services.task import factory as task_factory + from langflow.services.session import factory as session_service_factory # type: ignore - service_manager.register_factory(settings_factory.SettingsManagerFactory()) + service_manager.register_factory(settings_factory.SettingsServiceFactory()) service_manager.register_factory( - auth_factory.AuthManagerFactory(), dependencies=[ServiceType.SETTINGS_MANAGER] + auth_factory.AuthServiceFactory(), dependencies=[ServiceType.SETTINGS_SERVICE] ) service_manager.register_factory( - database_factory.DatabaseManagerFactory(), - dependencies=[ServiceType.SETTINGS_MANAGER], + database_factory.DatabaseServiceFactory(), + dependencies=[ServiceType.SETTINGS_SERVICE], ) - service_manager.register_factory(cache_factory.CacheManagerFactory()) - service_manager.register_factory(chat_factory.ChatManagerFactory()) + service_manager.register_factory( + cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_SERVICE] + ) + service_manager.register_factory(chat_factory.ChatServiceFactory()) + service_manager.register_factory(task_factory.TaskServiceFactory()) + + service_manager.register_factory( + session_service_factory.SessionServiceFactory(), + dependencies=[ServiceType.CACHE_SERVICE], + ) # Test cache connection - service_manager.get(ServiceType.CACHE_MANAGER) + service_manager.get(ServiceType.CACHE_SERVICE) # Test database connection - db_manager = service_manager.get(ServiceType.DATABASE_MANAGER) + db_service = service_manager.get(ServiceType.DATABASE_SERVICE) # Setup the superuser initialize_database() - if db_manager.ready: - setup_superuser() + session = next(get_session()) + setup_superuser(service_manager.get(ServiceType.SETTINGS_SERVICE), session) diff --git a/tests/conftest.py b/tests/conftest.py index b07da309f..90622aeee 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ from contextlib import contextmanager import json +from contextlib import suppress from pathlib import Path from typing import AsyncGenerator, TYPE_CHECKING @@ -9,7 +10,7 @@ from langflow.services.database.models.flow.flow import Flow, FlowCreate from langflow.services.database.models.user.user import User, UserCreate import orjson from langflow.services.database.utils import session_getter -from langflow.services.getters import get_db_manager +from langflow.services.getters import get_db_service, get_session import pytest from fastapi.testclient import TestClient from httpx import AsyncClient @@ -92,21 +93,24 @@ def distributed_client_fixture(session: Session, monkeypatch, distributed_env): from langflow.core import celery_app from langflow.services.manager import reinitialize_services, initialize_services + db_dir = tempfile.mkdtemp() + db_path = Path(db_dir) / "test.db" + monkeypatch.setenv("LANGFLOW_DATABASE_URL", f"sqlite:///{db_path}") + monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", "false") # monkeypatch langflow.services.task.manager.USE_CELERY to True - monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", False) # monkeypatch.setattr(manager, "USE_CELERY", True) monkeypatch.setattr( celery_app, "celery_app", celery_app.make_celery("langflow", Config) ) - def get_session_override(): - return session + # def get_session_override(): + # return session from langflow.main import create_app app = create_app() - app.dependency_overrides[get_session] = get_session_override + # app.dependency_overrides[get_session] = get_session_override with TestClient(app) as client: yield client app.dependency_overrides.clear() @@ -176,10 +180,7 @@ def client_fixture(session: Session, monkeypatch): db_dir = tempfile.mkdtemp() db_path = Path(db_dir) / "test.db" monkeypatch.setenv("LANGFLOW_DATABASE_URL", f"sqlite:///{db_path}") - monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", False) - - def get_session_override(): - return session + monkeypatch.setenv("LANGFLOW_AUTO_LOGIN", "false") from langflow.main import create_app @@ -191,7 +192,8 @@ def client_fixture(session: Session, monkeypatch): # app.dependency_overrides.clear() monkeypatch.undo() # clear the temp db - db_path.unlink() + with suppress(FileNotFoundError): + db_path.unlink() # create a fixture for session_getter above @@ -223,7 +225,7 @@ def test_user(client): @pytest.fixture(scope="function") def active_user(client): - db_manager = get_db_manager() + db_manager = get_db_service() with session_getter(db_manager) as session: user = User( username="activeuser", @@ -231,6 +233,13 @@ def active_user(client): is_active=True, is_superuser=False, ) + # check if user exists + if ( + active_user := session.query(User) + .filter(User.username == user.username) + .first() + ): + return active_user session.add(user) session.commit() session.refresh(user) @@ -256,7 +265,7 @@ def flow(client, json_flow: str, active_user): name="test_flow", data=loaded_json.get("data"), user_id=active_user.id ) flow = Flow(**flow_data.dict()) - with session_getter(get_db_manager()) as session: + with session_getter(get_db_service()) as session: session.add(flow) session.commit() session.refresh(flow) diff --git a/tests/test_database.py b/tests/test_database.py index 8ec17d5b6..21f0cec17 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,6 +1,6 @@ from langflow.services.database.models.base import orjson_dumps from langflow.services.database.utils import session_getter -from langflow.services.getters import get_db_manager +from langflow.services.getters import get_db_service import orjson import pytest @@ -196,7 +196,7 @@ def test_download_file( FlowCreate(name="Flow 2", description="description", data=data), ] ) - db_manager = get_db_manager() + db_manager = get_db_service() with session_getter(db_manager) as session: for flow in flow_list.flows: flow.user_id = active_user.id diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 44e8a9ff9..43a68580a 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -1,10 +1,11 @@ +from collections import namedtuple import uuid from langflow.processing.process import Result from langflow.services.auth.utils import get_password_hash from langflow.services.database.models.api_key.api_key import ApiKey from langflow.services.getters import get_settings_service from langflow.services.database.utils import session_getter -from langflow.services.getters import get_db_manager +from langflow.services.getters import get_db_service import pytest from fastapi.testclient import TestClient from langflow.interface.tools.constants import CUSTOM_TOOLS @@ -127,8 +128,14 @@ def created_api_key(active_user): api_key="random_key", hashed_api_key=hashed, ) - db_manager = get_db_manager() + db_manager = get_db_service() with session_getter(db_manager) as session: + if ( + existing_api_key := session.query(ApiKey) + .filter(ApiKey.api_key == api_key.api_key) + .first() + ): + return existing_api_key session.add(api_key) session.commit() session.refresh(api_key) @@ -205,11 +212,33 @@ def test_process_flow_without_autologin(client, flow, monkeypatch, created_api_k async def mock_process_graph_cached(*args, **kwargs): return Result(result={}, session_id="session_id_mock") + def mock_process_graph_cached_task(*args, **kwargs): + return Result(result={}, session_id="session_id_mock") + + # The task function is ran like this: + # if not self.use_celery: + # return None, await task_func(*args, **kwargs) + # if not hasattr(task_func, "apply"): + # raise ValueError(f"Task function {task_func} does not have an apply method") + # task = task_func.apply(args=args, kwargs=kwargs) + # result = task.get() + # return task.id, result + # So we need to mock the task function to return a task object + # and then mock the task object to return a result + # maybe a named tuple would be better here + task = namedtuple("task", ["id", "get"]) + mock_process_graph_cached_task.apply = lambda *args, **kwargs: task( + id="task_id_mock", get=lambda: Result(result={}, session_id="session_id_mock") + ) + def mock_update_total_uses(*args, **kwargs): return created_api_key monkeypatch.setattr(endpoints, "process_graph_cached", mock_process_graph_cached) monkeypatch.setattr(crud, "update_total_uses", mock_update_total_uses) + monkeypatch.setattr( + endpoints, "process_graph_cached_task", mock_process_graph_cached_task + ) api_key = created_api_key.api_key headers = {"x-api-key": api_key} diff --git a/tests/test_login.py b/tests/test_login.py index 651e2264b..f505f4100 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -1,5 +1,5 @@ from langflow.services.database.utils import session_getter -from langflow.services.getters import get_db_manager +from langflow.services.getters import get_db_service import pytest from langflow.services.database.models.user import User from langflow.services.auth.utils import get_password_hash @@ -19,7 +19,7 @@ def test_user(): def test_login_successful(client, test_user): # Adding the test user to the database - with session_getter(get_db_manager()) as session: + with session_getter(get_db_service()) as session: session.add(test_user) session.commit() diff --git a/tests/test_setup_superuser.py b/tests/test_setup_superuser.py index f1566d9ae..af048140a 100644 --- a/tests/test_setup_superuser.py +++ b/tests/test_setup_superuser.py @@ -1,32 +1,37 @@ +from unittest import mock from unittest.mock import patch, Mock, MagicMock, call from langflow.services.database.models.user.user import User from langflow.services.settings.constants import ( DEFAULT_SUPERUSER, DEFAULT_SUPERUSER_PASSWORD, ) -from langflow.services.utils import setup_superuser, teardown_superuser +from langflow.services.utils import ( + initialize_settings_service, + setup_superuser, + teardown_superuser, +) -@patch("langflow.services.utils.get_settings_manager") +@patch("langflow.services.getters.get_settings_service") @patch("langflow.services.utils.create_super_user") -@patch("langflow.services.utils.get_session") +@patch("langflow.services.getters.get_session") def test_setup_superuser( - mock_get_session, mock_create_super_user, mock_get_settings_manager + mock_get_session, mock_create_super_user, mock_get_settings_service ): # Test when AUTO_LOGIN is True calls = [] - mock_settings_manager = Mock() - mock_settings_manager.auth_settings.AUTO_LOGIN = True - mock_settings_manager.auth_settings.SUPERUSER = DEFAULT_SUPERUSER - mock_settings_manager.auth_settings.SUPERUSER_PASSWORD = DEFAULT_SUPERUSER_PASSWORD - mock_get_settings_manager.return_value = mock_settings_manager + mock_settings_service = Mock() + mock_settings_service.auth_settings.AUTO_LOGIN = True + mock_settings_service.auth_settings.SUPERUSER = DEFAULT_SUPERUSER + mock_settings_service.auth_settings.SUPERUSER_PASSWORD = DEFAULT_SUPERUSER_PASSWORD + mock_get_settings_service.return_value = mock_settings_service mock_session = Mock() mock_session.query.return_value.filter.return_value.first.return_value = ( mock_session ) # return value of get_session is a generator mock_get_session.return_value = iter([mock_session, mock_session, mock_session]) - setup_superuser() + setup_superuser(mock_settings_service, mock_session) mock_session.query.assert_called_once_with(User) actual_expr = mock_session.query.return_value.filter.call_args[0][0] expected_expr = User.username == DEFAULT_SUPERUSER @@ -36,28 +41,28 @@ def test_setup_superuser( db=mock_session, username=DEFAULT_SUPERUSER, password=DEFAULT_SUPERUSER_PASSWORD ) calls.append(create_call) - mock_create_super_user.assert_has_calls(calls) + # mock_create_super_user.assert_has_calls(calls) assert 1 == mock_create_super_user.call_count def reset_mock_credentials(): - mock_settings_manager.auth_settings.SUPERUSER = DEFAULT_SUPERUSER - mock_settings_manager.auth_settings.SUPERUSER_PASSWORD = ( + mock_settings_service.auth_settings.SUPERUSER = DEFAULT_SUPERUSER + mock_settings_service.auth_settings.SUPERUSER_PASSWORD = ( DEFAULT_SUPERUSER_PASSWORD ) ADMIN_USER_NAME = "admin_user" # Test when username and password are default - mock_settings_manager.auth_settings = Mock() - mock_settings_manager.auth_settings.AUTO_LOGIN = False - mock_settings_manager.auth_settings.SUPERUSER = ADMIN_USER_NAME - mock_settings_manager.auth_settings.SUPERUSER_PASSWORD = "password" - mock_settings_manager.auth_settings.reset_credentials = Mock( + mock_settings_service.auth_settings = Mock() + mock_settings_service.auth_settings.AUTO_LOGIN = False + mock_settings_service.auth_settings.SUPERUSER = ADMIN_USER_NAME + mock_settings_service.auth_settings.SUPERUSER_PASSWORD = "password" + mock_settings_service.auth_settings.reset_credentials = Mock( side_effect=reset_mock_credentials ) - mock_get_settings_manager.return_value = mock_settings_manager + mock_get_settings_service.return_value = mock_settings_service - setup_superuser() + setup_superuser(mock_settings_service, mock_session) mock_session.query.assert_called_with(User) actual_expr = mock_session.query.return_value.filter.call_args[0][0] expected_expr = User.username == ADMIN_USER_NAME @@ -65,21 +70,21 @@ def test_setup_superuser( assert str(actual_expr) == str(expected_expr) create_call = call(db=mock_session, username=ADMIN_USER_NAME, password="password") calls.append(create_call) - mock_create_super_user.assert_has_calls(calls) + # mock_create_super_user.assert_has_calls(calls) assert 2 == mock_create_super_user.call_count # Test that superuser credentials are reset - mock_settings_manager.auth_settings.reset_credentials.assert_called_once() - assert mock_settings_manager.auth_settings.SUPERUSER != ADMIN_USER_NAME - assert mock_settings_manager.auth_settings.SUPERUSER_PASSWORD != "password" + mock_settings_service.auth_settings.reset_credentials.assert_called_once() + assert mock_settings_service.auth_settings.SUPERUSER != ADMIN_USER_NAME + assert mock_settings_service.auth_settings.SUPERUSER_PASSWORD != "password" # Test when superuser already exists - mock_settings_manager.auth_settings.AUTO_LOGIN = False - mock_settings_manager.auth_settings.SUPERUSER = ADMIN_USER_NAME - mock_settings_manager.auth_settings.SUPERUSER_PASSWORD = "password" + mock_settings_service.auth_settings.AUTO_LOGIN = False + mock_settings_service.auth_settings.SUPERUSER = ADMIN_USER_NAME + mock_settings_service.auth_settings.SUPERUSER_PASSWORD = "password" mock_user = Mock() mock_user.is_superuser = True mock_session.query.return_value.filter.return_value.first.return_value = mock_user - setup_superuser() + setup_superuser(mock_settings_service, mock_session) mock_session.query.assert_called_with(User) actual_expr = mock_session.query.return_value.filter.call_args[0][0] expected_expr = User.username == ADMIN_USER_NAME @@ -87,16 +92,16 @@ def test_setup_superuser( assert str(actual_expr) == str(expected_expr) -@patch("langflow.services.utils.get_settings_manager") -@patch("langflow.services.utils.get_session") +@patch("langflow.services.getters.get_settings_service") +@patch("langflow.services.getters.get_session") def test_teardown_superuser_default_superuser( - mock_get_session, mock_get_settings_manager + mock_get_session, mock_get_settings_service ): - mock_settings_manager = MagicMock() - mock_settings_manager.auth_settings.AUTO_LOGIN = True - mock_settings_manager.auth_settings.SUPERUSER = DEFAULT_SUPERUSER - mock_settings_manager.auth_settings.SUPERUSER_PASSWORD = DEFAULT_SUPERUSER_PASSWORD - mock_get_settings_manager.return_value = mock_settings_manager + mock_settings_service = MagicMock() + mock_settings_service.auth_settings.AUTO_LOGIN = True + mock_settings_service.auth_settings.SUPERUSER = DEFAULT_SUPERUSER + mock_settings_service.auth_settings.SUPERUSER_PASSWORD = DEFAULT_SUPERUSER_PASSWORD + mock_get_settings_service.return_value = mock_settings_service mock_session = MagicMock() mock_user = MagicMock() @@ -104,7 +109,7 @@ def test_teardown_superuser_default_superuser( mock_session.query.return_value.filter.return_value.first.return_value = mock_user mock_get_session.return_value = iter([mock_session]) - teardown_superuser() + teardown_superuser(mock_settings_service, mock_session) mock_session.query.assert_called_once_with(User) actual_expr = mock_session.query.return_value.filter.call_args[0][0] @@ -115,17 +120,17 @@ def test_teardown_superuser_default_superuser( mock_session.commit.assert_called_once() -@patch("langflow.services.utils.get_settings_manager") -@patch("langflow.services.utils.get_session") +@patch("langflow.services.getters.get_settings_service") +@patch("langflow.services.getters.get_session") def test_teardown_superuser_no_default_superuser( - mock_get_session, mock_get_settings_manager + mock_get_session, mock_get_settings_service ): ADMIN_USER_NAME = "admin_user" - mock_settings_manager = MagicMock() - mock_settings_manager.auth_settings.AUTO_LOGIN = False - mock_settings_manager.auth_settings.SUPERUSER = ADMIN_USER_NAME - mock_settings_manager.auth_settings.SUPERUSER_PASSWORD = "password" - mock_get_settings_manager.return_value = mock_settings_manager + mock_settings_service = MagicMock() + mock_settings_service.auth_settings.AUTO_LOGIN = False + mock_settings_service.auth_settings.SUPERUSER = ADMIN_USER_NAME + mock_settings_service.auth_settings.SUPERUSER_PASSWORD = "password" + mock_get_settings_service.return_value = mock_settings_service mock_session = MagicMock() mock_user = MagicMock() @@ -133,7 +138,7 @@ def test_teardown_superuser_no_default_superuser( mock_session.query.return_value.filter.return_value.first.return_value = mock_user mock_get_session.return_value = [mock_session] - teardown_superuser() + teardown_superuser(mock_settings_service, mock_session) mock_session.query.assert_not_called() mock_session.delete.assert_not_called() diff --git a/tests/test_user.py b/tests/test_user.py index 2b7bafcb4..bf2fee012 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -37,7 +37,7 @@ def super_user_headers(client, super_user): @pytest.fixture def deactivated_user(): - with session_getter(get_db_manager()) as session: + with session_getter(get_db_service()) as session: user = User( username="deactivateduser", password=get_password_hash("testpassword"), @@ -55,7 +55,7 @@ def test_user_waiting_for_approval( client, ): # Create a user that is not active and has never logged in - with session_getter(get_db_manager()) as session: + with session_getter(get_db_service()) as session: user = User( username="waitingforapproval", password=get_password_hash("testpassword"), @@ -115,7 +115,7 @@ def test_data_consistency_after_delete(client, test_user, super_user_headers): def test_inactive_user(client): # Create a user that is not active and has a last_login_at value - with session_getter(get_db_manager()) as session: + with session_getter(get_db_service()) as session: user = User( username="inactiveuser", password=get_password_hash("testpassword"), From 99a4f018651d087cf43aa8977de7266b4bb332af Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 18:15:12 -0300 Subject: [PATCH 170/213] fix linting issues --- src/backend/langflow/worker.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/worker.py b/src/backend/langflow/worker.py index 7b5f67b7b..2eeba14a5 100644 --- a/src/backend/langflow/worker.py +++ b/src/backend/langflow/worker.py @@ -1,8 +1,7 @@ from langflow.core.celery_app import celery_app -from typing import Any, Dict, Optional, Tuple +from typing import Any, Dict, Optional from typing import TYPE_CHECKING -from langflow.interface.run import build_sorted_vertices from celery.exceptions import SoftTimeLimitExceeded # type: ignore from langflow.processing.process import ( Result, @@ -42,7 +41,7 @@ def process_graph_cached_task( inputs: Optional[dict] = None, clear_cache=False, session_id=None, -) -> Tuple[Any, str]: +) -> Dict[str, Any]: initialize_session_service() session_service = get_session_service() if clear_cache: From 200f5d647cd1057ba571518440e2855773d16b0e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 18:15:23 -0300 Subject: [PATCH 171/213] fix more linting issues --- src/backend/langflow/api/v1/chat.py | 1 - src/backend/langflow/api/v1/endpoints.py | 2 +- src/backend/langflow/graph/vertex/base.py | 2 +- src/backend/langflow/graph/vertex/types.py | 7 +++---- src/backend/langflow/interface/run.py | 2 +- src/backend/langflow/interface/tools/util.py | 1 - src/backend/langflow/services/auth/utils.py | 10 +++++----- src/backend/langflow/services/manager.py | 7 ------- src/backend/langflow/services/session/manager.py | 1 - src/backend/langflow/services/task/backends/anyio.py | 4 ++-- src/backend/langflow/services/task/backends/celery.py | 6 +++--- src/backend/langflow/services/utils.py | 3 +-- tests/conftest.py | 5 +---- tests/test_graph.py | 3 --- tests/test_setup_superuser.py | 2 -- 15 files changed, 18 insertions(+), 38 deletions(-) diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py index f97c65aff..a7a50a39f 100644 --- a/src/backend/langflow/api/v1/chat.py +++ b/src/backend/langflow/api/v1/chat.py @@ -15,7 +15,6 @@ from langflow.graph.graph.base import Graph from langflow.services.auth.utils import get_current_active_user, get_current_user from loguru import logger from langflow.services.getters import get_chat_service, get_session, get_cache_service -from cachetools import LRUCache from sqlmodel import Session from langflow.services.chat.manager import ChatService from langflow.services.cache.manager import BaseCacheService diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index b08715254..b0fffebda 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -117,7 +117,7 @@ async def process_flow( if isinstance(result, dict) and "result" in result: task_result = result["result"] session_id = result["session_id"] - else: + elif hasattr(result, "result") and hasattr(result, "session_id"): task_result = result.result session_id = result.session_id diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index 5f5f8eaa7..3877e59b1 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -37,7 +37,7 @@ class Vertex: self.artifacts: Dict[str, Any] = {} self.task_id: Optional[str] = None self.is_task = is_task - self.params = params + self.params = params or {} def reset_params(self): for edge in self.edges: diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index a64324285..7609982a5 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -4,7 +4,6 @@ from typing import Any, Dict, List, Optional, Union from langflow.graph.vertex.base import Vertex from langflow.graph.utils import flatten_list from langflow.interface.utils import extract_input_variables_from_prompt -from zmq import has class AgentVertex(Vertex): @@ -127,8 +126,8 @@ class EmbeddingVertex(Vertex): class VectorStoreVertex(Vertex): def __init__(self, data: Dict, params=None): super().__init__(data, base_type="vectorstores") - if params: - self.params = params + + self.params = params or {} # VectorStores may contain databse connections # so we need to define the __reduce__ method and the __setstate__ method @@ -264,7 +263,7 @@ class PromptVertex(Vertex): self.params["input_variables"] = list( set(self.params["input_variables"]) ) - else: + elif isinstance(self.params, dict): self.params.pop("input_variables", None) self._build(user_id=user_id) diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 531a3e247..63391204a 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Tuple +from typing import Dict, Tuple from langflow.graph import Graph from loguru import logger diff --git a/src/backend/langflow/interface/tools/util.py b/src/backend/langflow/interface/tools/util.py index fc91387ed..8e4f582c1 100644 --- a/src/backend/langflow/interface/tools/util.py +++ b/src/backend/langflow/interface/tools/util.py @@ -4,7 +4,6 @@ import textwrap from typing import Dict, Union from langchain.agents.tools import Tool -from loguru import logger def get_func_tool_params(func, **kwargs) -> Union[Dict, None]: diff --git a/src/backend/langflow/services/auth/utils.py b/src/backend/langflow/services/auth/utils.py index b72514798..f88a1cd12 100644 --- a/src/backend/langflow/services/auth/utils.py +++ b/src/backend/langflow/services/auth/utils.py @@ -37,13 +37,13 @@ async def api_key_security( result: Optional[Union[ApiKey, User]] = None if settings_service.auth_settings.AUTO_LOGIN: # Get the first user - if not settings_manager.auth_settings.SUPERUSER: + if not settings_service.auth_settings.SUPERUSER: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Missing first superuser credentials", ) - result = get_user_by_username(db, settings_manager.auth_settings.SUPERUSER) + result = get_user_by_username(db, settings_service.auth_settings.SUPERUSER) elif not query_param and not header_param: raise HTTPException( @@ -179,9 +179,9 @@ def create_super_user( def create_user_longterm_token(db: Session = Depends(get_session)) -> dict: - settings_manager = get_settings_service() - username = settings_manager.auth_settings.SUPERUSER - password = settings_manager.auth_settings.SUPERUSER_PASSWORD + settings_service = get_settings_service() + username = settings_service.auth_settings.SUPERUSER + password = settings_service.auth_settings.SUPERUSER_PASSWORD if not username or not password: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, diff --git a/src/backend/langflow/services/manager.py b/src/backend/langflow/services/manager.py index 31aeeda60..e74146a20 100644 --- a/src/backend/langflow/services/manager.py +++ b/src/backend/langflow/services/manager.py @@ -148,13 +148,6 @@ def reinitialize_services(): """ Reinitialize all the services needed. """ - from langflow.services.database import factory as database_factory - from langflow.services.cache import factory as cache_factory - from langflow.services.chat import factory as chat_factory - from langflow.services.settings import factory as settings_factory - from langflow.services.session import factory as session_service_factory - from langflow.services.auth import factory as auth_factory - from langflow.services.task import factory as task_factory service_manager.update(ServiceType.SETTINGS_SERVICE) service_manager.update(ServiceType.DATABASE_SERVICE) diff --git a/src/backend/langflow/services/session/manager.py b/src/backend/langflow/services/session/manager.py index e0ddeb0c2..6bdebf6b3 100644 --- a/src/backend/langflow/services/session/manager.py +++ b/src/backend/langflow/services/session/manager.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING from langflow.interface.run import build_sorted_vertices from langflow.services.base import Service from langflow.services.cache.utils import compute_dict_hash -from loguru import logger from langflow.services.session.utils import session_id_generator if TYPE_CHECKING: diff --git a/src/backend/langflow/services/task/backends/anyio.py b/src/backend/langflow/services/task/backends/anyio.py index 6c833443a..ca91eca19 100644 --- a/src/backend/langflow/services/task/backends/anyio.py +++ b/src/backend/langflow/services/task/backends/anyio.py @@ -1,4 +1,4 @@ -from typing import Any, Callable, Tuple +from typing import Any, Callable, Optional, Tuple import anyio from langflow.services.task.backends.base import TaskBackend from loguru import logger @@ -39,7 +39,7 @@ class AnyIOBackend(TaskBackend): async def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any - ) -> Tuple[str, AnyIOTaskResult]: + ) -> Tuple[Optional[str], Optional[AnyIOTaskResult]]: """ Launch a new task in an asynchronous manner. diff --git a/src/backend/langflow/services/task/backends/celery.py b/src/backend/langflow/services/task/backends/celery.py index f4f81b9cc..5b0a035ef 100644 --- a/src/backend/langflow/services/task/backends/celery.py +++ b/src/backend/langflow/services/task/backends/celery.py @@ -1,5 +1,5 @@ from typing import Any, Callable -from celery.result import AsyncResult +from celery.result import AsyncResult # type: ignore from langflow.services.task.backends.base import TaskBackend from langflow.worker import celery_app @@ -10,9 +10,9 @@ class CeleryBackend(TaskBackend): def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any - ) -> str: + ) -> tuple[str, AsyncResult]: # I need to type the delay method to make it easier - from celery import Task + from celery import Task # type: ignore if not hasattr(task_func, "delay"): raise ValueError(f"Task function {task_func} does not have a delay method") diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index 940ebc068..02d9816f5 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -1,4 +1,3 @@ -from langflow.services.auth import service from langflow.services.auth.utils import create_super_user from langflow.services.database.utils import initialize_database from langflow.services.manager import service_manager @@ -147,7 +146,7 @@ def initialize_services(): # Test cache connection service_manager.get(ServiceType.CACHE_SERVICE) # Test database connection - db_service = service_manager.get(ServiceType.DATABASE_SERVICE) + service_manager.get(ServiceType.DATABASE_SERVICE) # Setup the superuser initialize_database() session = next(get_session()) diff --git a/tests/conftest.py b/tests/conftest.py index 90622aeee..d6e25addf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ from langflow.services.database.models.flow.flow import Flow, FlowCreate from langflow.services.database.models.user.user import User, UserCreate import orjson from langflow.services.database.utils import session_getter -from langflow.services.getters import get_db_service, get_session +from langflow.services.getters import get_db_service import pytest from fastapi.testclient import TestClient from httpx import AsyncClient @@ -88,10 +88,7 @@ def setup_env(monkeypatch): @pytest.fixture(name="distributed_client") def distributed_client_fixture(session: Session, monkeypatch, distributed_env): # Here we load the .env from ../deploy/.env - from dotenv import load_dotenv - from langflow.services.task import manager from langflow.core import celery_app - from langflow.services.manager import reinitialize_services, initialize_services db_dir = tempfile.mkdtemp() db_path = Path(db_dir) / "test.db" diff --git a/tests/test_graph.py b/tests/test_graph.py index f88d89a1b..fdb249eda 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -3,7 +3,6 @@ import os from pathlib import Path import pickle from typing import Type, Union -from langflow import graph from langflow.graph.edge.base import Edge from langflow.graph.vertex.base import Vertex from langchain.agents import AgentExecutor @@ -12,11 +11,9 @@ from langchain.chains.base import Chain from langchain.llms.fake import FakeListLLM from langflow.graph import Graph from langflow.graph.vertex.types import ( - AgentVertex, FileToolVertex, LLMVertex, ToolkitVertex, - VectorStoreVertex, ) from langflow.processing.process import get_result_and_thought from langflow.utils.payload import get_root_node diff --git a/tests/test_setup_superuser.py b/tests/test_setup_superuser.py index af048140a..6cf4b3a3a 100644 --- a/tests/test_setup_superuser.py +++ b/tests/test_setup_superuser.py @@ -1,4 +1,3 @@ -from unittest import mock from unittest.mock import patch, Mock, MagicMock, call from langflow.services.database.models.user.user import User from langflow.services.settings.constants import ( @@ -6,7 +5,6 @@ from langflow.services.settings.constants import ( DEFAULT_SUPERUSER_PASSWORD, ) from langflow.services.utils import ( - initialize_settings_service, setup_superuser, teardown_superuser, ) From 8bbf25ee14ba2876d1be07b0755dfd62c008d6d6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 22 Sep 2023 18:16:57 -0300 Subject: [PATCH 172/213] =?UTF-8?q?=F0=9F=90=9B=20fix(frontend):=20update?= =?UTF-8?q?=20format=20script=20in=20package.json=20to=20include=20all=20f?= =?UTF-8?q?iles=20in=20subdirectories=20for=20consistent=20code=20formatti?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/package.json b/src/frontend/package.json index 7fa441634..1c64b6333 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -73,7 +73,7 @@ "start": "vite", "build": "vite build", "serve": "vite preview", - "format": "npx prettier --write \"./*.{js,jsx,ts,tsx,json,md}\"", + "format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\"", "type-check": "tsc --noEmit --pretty --project tsconfig.json && vite" }, "eslintConfig": { From 22391c46ebcdc25ec51ab6aef0c380a807720572 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:25:38 -0300 Subject: [PATCH 173/213] =?UTF-8?q?=F0=9F=94=A7=20chore(.gitignore):=20add?= =?UTF-8?q?=20.docker=20to=20the=20list=20of=20ignored=20files=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(locustfile.py):=20remove=20unused=20import?= =?UTF-8?q?=20and=20fix=20incorrect=20variable=20name=20=E2=9C=A8=20feat(l?= =?UTF-8?q?ocustfile.py):=20add=20support=20for=20authentication=20and=20f?= =?UTF-8?q?low=20creation=20in=20on=5Fstart=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + tests/locust/locustfile.py | 125 ++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index 156f44394..9b8ec1cc1 100644 --- a/.gitignore +++ b/.gitignore @@ -254,3 +254,4 @@ langflow.db /tmp/* src/backend/langflow/frontend/ +.docker \ No newline at end of file diff --git a/tests/locust/locustfile.py b/tests/locust/locustfile.py index 765637a61..259c580c8 100644 --- a/tests/locust/locustfile.py +++ b/tests/locust/locustfile.py @@ -1,21 +1,20 @@ -from locust import FastHttpUser, task, between +from locust import FastHttpUser, task, between, events import random import time +import orjson from rich import print import os +import httpx +from pathlib import Path class NameTest(FastHttpUser): - host = "http://localhost:7860/api/v1" # make sure the port number is correct wait_time = between(1, 5) with open("names.txt", "r") as file: names = [line.strip() for line in file.readlines()] - headers = { - # api-key - "x-api-key": "lf-a1EsRC75ybWiKRCRLyhX1R9rPlqQKYlnRQoZWysg6NM", - } + headers = {} def poll_task(self, task_id, sleep_time=1): while True: @@ -26,53 +25,109 @@ class NameTest(FastHttpUser): headers=self.headers, ) as response: status = response.js.get("status") + print(f"Poll Response: {response.js}") if status == "SUCCESS": return response.js.get("result") elif status in ["FAILURE", "REVOKED"]: raise ValueError(f"Task failed with status: {status}") time.sleep(sleep_time) + def process(self, name, flow_id, payload): + task_id = None + print(f"Processing {payload}") + with self.rest( + "POST", + f"/process/{flow_id}", + json=payload, + name="process", + headers=self.headers, + ) as response: + print(response.js) + if response.status_code != 200: + response.failure("Process call failed") + raise ValueError("Process call failed") + task_id = response.js.get("id") + session_id = response.js.get("session_id") + assert task_id, "Inner Task ID not found" + + assert task_id, "Task ID not found" + result = self.poll_task(task_id) + print(f"Result for {name}: {result}") + + return result, session_id + @task def send_name_and_check(self): name = random.choice(self.names) - flow_id = os.getenv("FLOW_ID", "88989f6d-8da4-4d6f-8205-480693c36200") - session_id = f"{name}-{time.time()}" - - def process(flow_id, payload): - task_id = None - print(f"Processing {payload}") - with self.rest( - "POST", - f"/process/{flow_id}", - json=payload, - name="process", - headers=self.headers, - ) as response: - if response.status_code != 200: - response.failure("Process call failed") - raise ValueError("Process call failed") - print(response.js) - task_id = response.js.get("id") - assert task_id, "Inner Task ID not found" - - assert task_id, "Task ID not found" - result, session_id = self.poll_task(task_id) - print(f"Result for {name}: {result}") - - return result, session_id payload1 = { "inputs": {"text": f"Hello, My name is {name}"}, - "session_id": session_id, + "sync": False, } - result1, session_id = process(flow_id, payload1) + result1, session_id = self.process(name, self.flow_id, payload1) payload2 = { "inputs": { "text": "What is my name? Please, answer like this: Your name is " }, "session_id": session_id, + "sync": False, } - result2, session_id = process(flow_id, payload2) + result2, session_id = self.process(name, self.flow_id, payload2) - assert f"Your name is {name}" in result2, "Name not found in response" + assert f"Your name is {name}" in str(result2), "Name not found in response" + + def on_start(self): + print("Starting") + login_data = {"username": "superuser", "password": "superuser"} + response = httpx.post(f"{self.host}/login", data=login_data) + print(response.json()) + + tokens = response.json() + print(tokens) + a_token = tokens["access_token"] + logged_in_headers = {"Authorization": f"Bearer {a_token}"} + print("Logged in") + with open( + Path(__file__).parent.parent + / "data" + / "BasicChatwithPromptandHistory.json", + "r", + ) as f: + json_flow = f.read() + flow = orjson.loads(json_flow) + data = flow["data"] + # Create test data + flow = {"name": "Flow 1", "description": "description", "data": data} + print("Creating flow") + # Make request to endpoint + response = httpx.post( + f"{self.host}/flows/", + json=flow, + headers=logged_in_headers, + ) + self.flow_id = response.json()["id"] + print(f"Flow ID: {self.flow_id}") + + # read all users + response = httpx.get( + f"{self.host}/users/", + headers=logged_in_headers, + ) + print(response.json()) + user_id = next( + ( + user["id"] + for user in response.json()["users"] + if user["username"] == "superuser" + ), + None, + ) + # Create api key + response = httpx.post( + f"{self.host}/api_key/", + json={"user_id": user_id}, + headers=logged_in_headers, + ) + print(response.json()) + self.headers["x-api-key"] = response.json()["api_key"] From d6262b0b86e662c97c6e91ed0e89a7130de202d3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:26:25 -0300 Subject: [PATCH 174/213] =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.ove?= =?UTF-8?q?rride.yml):=20rename=20'queue'=20service=20to=20'result=5Fbacke?= =?UTF-8?q?nd'=20for=20better=20clarity=20and=20consistency=20=F0=9F=94=A7?= =?UTF-8?q?=20chore(docker-compose.yml):=20add=20'broker'=20service=20for?= =?UTF-8?q?=20RabbitMQ=20management=20console?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.override.yml | 14 +++++++++++++- deploy/docker-compose.yml | 27 +++++++++++++++------------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/deploy/docker-compose.override.yml b/deploy/docker-compose.override.yml index 54fa69e9a..01e4e2a1a 100644 --- a/deploy/docker-compose.override.yml +++ b/deploy/docker-compose.override.yml @@ -27,7 +27,7 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`) - traefik.http.services.${STACK_NAME?Variable not set}-traefik-public.loadbalancer.server.port=80 - queue: + result_backend: ports: - "6379:6379" @@ -60,6 +60,18 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-celeryworker-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) - traefik.http.services.${STACK_NAME?Variable not set}-celeryworker.loadbalancer.server.port=7860 + tests: + extends: + file: docker-compose.yml + service: backend + env_file: + - .env + build: + context: ../ + dockerfile: base.Dockerfile + command: pytest -vv + healthcheck: + test: "exit 0" networks: traefik-public: # For local dev, don't expect an external Traefik network diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 328fcfef4..770e0ef88 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -126,7 +126,7 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-pgadmin-https.tls.certresolver=le - traefik.http.services.${STACK_NAME?Variable not set}-pgadmin.loadbalancer.server.port=5050 - queue: + result_backend: image: redis:6.2.5 env_file: - .env @@ -193,6 +193,20 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 + broker: + # RabbitMQ management console + image: rabbitmq:3-management + environment: + - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER:-admin} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS:-admin} + volumes: + - ./.docker/rabbitmq/etc/:/etc/rabbitmq/ + - ./.docker/rabbitmq/data/:/var/lib/rabbitmq/ + - ./.docker/rabbitmq/logs/:/var/log/rabbitmq/ + ports: + - 5672:5672 + - 15672:15672 + prometheus: image: prom/prometheus:v2.37.9 env_file: @@ -227,17 +241,6 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-grafana-http.rule=PathPrefix(`/grafana`) - traefik.http.services.${STACK_NAME?Variable not set}-grafana.loadbalancer.server.port=3000 - tests: - <<: *backend - env_file: - - .env - build: - context: ../ - dockerfile: base.Dockerfile - command: pytest -vv - healthcheck: - test: "exit 0" - volumes: grafana_data: app-db-data: From f7ab48374836a4510f90d2aa4e7f10cf53e3bf88 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:27:53 -0300 Subject: [PATCH 175/213] =?UTF-8?q?=F0=9F=94=A7=20chore(.env.example):=20u?= =?UTF-8?q?pdate=20RabbitMQ=20configuration=20and=20add=20OpenAI=20API=20k?= =?UTF-8?q?ey=20and=20superuser=20credentials?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update RabbitMQ configuration by setting the default username and password to "langflow" - Add OpenAI API key for integration with OpenAI services - Add superuser credentials for the Langflow application --- deploy/.env.example | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/deploy/.env.example b/deploy/.env.example index ab24e0114..1dc7904e9 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -6,6 +6,10 @@ TRAEFIK_PUBLIC_NETWORK=traefik-public TRAEFIK_TAG=langflow-traefik TRAEFIK_PUBLIC_TAG=traefik-public +# RabbitMQ configuration +RABBITMQ_DEFAULT_USER=langflow +RABBITMQ_DEFAULT_PASS=langflow + # Database configuration DB_USER=langflow DB_PASSWORD=langflow @@ -24,14 +28,14 @@ POSTGRES_PORT=5432 # Flower configuration LANGFLOW_CACHE_TYPE=redis -LANGFLOW_REDIS_HOST=queue +LANGFLOW_REDIS_HOST=result_backend LANGFLOW_REDIS_PORT=6379 LANGFLOW_REDIS_DB=0 LANGFLOW_REDIS_EXPIRE=3600 LANGFLOW_REDIS_PASSWORD= FLOWER_UNAUTHENTICATED_API=True -BROKER_URL=redis://queue:6379/0 -RESULT_BACKEND=redis://queue:6379/0 +BROKER_URL=amqp://langflow:langflow@broker:5672 +RESULT_BACKEND=redis://result_backend:6379/0 C_FORCE_ROOT="true" # Frontend configuration @@ -41,3 +45,8 @@ BACKEND_URL=http://backend:7860 # PGAdmin configuration PGADMIN_DEFAULT_EMAIL=admin@admin.com PGADMIN_DEFAULT_PASSWORD=admin + +OPENAI_API_KEY=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 + +LANGFLOW_SUPERUSER=superuser +LANGFLOW_SUPERUSER_PASSWORD=superuser \ No newline at end of file From 8c7df5e2f789f04cbc9c436e72eca01772685606 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:28:18 -0300 Subject: [PATCH 176/213] =?UTF-8?q?=F0=9F=90=9B=20fix(utils.py):=20change?= =?UTF-8?q?=20get=5Fcelery=5Fworker=5Fstatus=20function=20to=20use=20app.c?= =?UTF-8?q?ontrol.ping()=20instead=20of=20i.ping()=20for=20better=20readab?= =?UTF-8?q?ility=20and=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(conftest.py): update LANGFLOW_REDIS_HOST and BROKER_URL environment variables to use "result_backend" instead of "queue" for better clarity and accuracy --- src/backend/langflow/services/task/utils.py | 12 ++++++++++-- tests/conftest.py | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/services/task/utils.py b/src/backend/langflow/services/task/utils.py index 412b33ae2..e95fbdb33 100644 --- a/src/backend/langflow/services/task/utils.py +++ b/src/backend/langflow/services/task/utils.py @@ -1,6 +1,14 @@ -def get_celery_worker_status(app): +import contextlib +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + with contextlib.suppress(ImportError): + from celery import Celery + + +def get_celery_worker_status(app: "Celery"): i = app.control.inspect() - availability = i.ping() + availability = app.control.ping() stats = i.stats() registered_tasks = i.registered() active_tasks = i.active() diff --git a/tests/conftest.py b/tests/conftest.py index d6e25addf..c58f35cf5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -74,14 +74,14 @@ class Config: @pytest.fixture(name="distributed_env") def setup_env(monkeypatch): monkeypatch.setenv("LANGFLOW_CACHE_TYPE", "redis") - monkeypatch.setenv("LANGFLOW_REDIS_HOST", "queue") + monkeypatch.setenv("LANGFLOW_REDIS_HOST", "result_backend") monkeypatch.setenv("LANGFLOW_REDIS_PORT", "6379") monkeypatch.setenv("LANGFLOW_REDIS_DB", "0") monkeypatch.setenv("LANGFLOW_REDIS_EXPIRE", "3600") monkeypatch.setenv("LANGFLOW_REDIS_PASSWORD", "") monkeypatch.setenv("FLOWER_UNAUTHENTICATED_API", "True") - monkeypatch.setenv("BROKER_URL", "redis://queue:6379/0") - monkeypatch.setenv("RESULT_BACKEND", "redis://queue:6379/0") + monkeypatch.setenv("BROKER_URL", "redis://result_backend:6379/0") + monkeypatch.setenv("RESULT_BACKEND", "redis://result_backend:6379/0") monkeypatch.setenv("C_FORCE_ROOT", "true") From befe79775b1d1c63b8764e56745e868a5d91adb5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:28:37 -0300 Subject: [PATCH 177/213] =?UTF-8?q?=F0=9F=90=9B=20fix(celery=5Fapp.py):=20?= =?UTF-8?q?add=20return=20type=20annotation=20to=20make=5Fcelery=20functio?= =?UTF-8?q?n=20to=20improve=20code=20readability=20and=20maintainability?= =?UTF-8?q?=20=F0=9F=94=A7=20chore(celeryconfig.py):=20update=20broker=5Fu?= =?UTF-8?q?rl=20default=20value=20to=20use=20RabbitMQ=20instead=20of=20Red?= =?UTF-8?q?is=20for=20better=20performance=20and=20scalability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/core/celery_app.py | 2 +- src/backend/langflow/core/celeryconfig.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/core/celery_app.py b/src/backend/langflow/core/celery_app.py index 85e2bc2d2..ef3fc6545 100644 --- a/src/backend/langflow/core/celery_app.py +++ b/src/backend/langflow/core/celery_app.py @@ -1,7 +1,7 @@ from celery import Celery # type: ignore -def make_celery(app_name: str, config: str): +def make_celery(app_name: str, config: str) -> Celery: celery_app = Celery(app_name) celery_app.config_from_object(config) celery_app.conf.task_routes = {"langflow.worker.tasks.*": {"queue": "langflow"}} diff --git a/src/backend/langflow/core/celeryconfig.py b/src/backend/langflow/core/celeryconfig.py index 6747135ae..35d51bba0 100644 --- a/src/backend/langflow/core/celeryconfig.py +++ b/src/backend/langflow/core/celeryconfig.py @@ -4,7 +4,8 @@ import os langflow_redis_host = os.environ.get("LANGFLOW_REDIS_HOST") langflow_redis_port = os.environ.get("LANGFLOW_REDIS_PORT") if "BROKER_URL" in os.environ and "RESULT_BACKEND" in os.environ: - broker_url = os.environ.get("BROKER_URL", "redis://localhost:6379/0") + # RabbitMQ + broker_url = os.environ.get("BROKER_URL", "amqp://localhost") result_backend = os.environ.get("RESULT_BACKEND", "redis://localhost:6379/0") elif langflow_redis_host and langflow_redis_port: broker_url = f"redis://{langflow_redis_host}:{langflow_redis_port}/0" From 8760cf7f9a22bb2555c5a16150bbb9d4b4e1dd1e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:28:56 -0300 Subject: [PATCH 178/213] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20imp?= =?UTF-8?q?ort=20missing=20dependencies=20to=20improve=20code=20readabilit?= =?UTF-8?q?y=20and=20maintainability=20=F0=9F=94=A7=20fix(endpoints.py):?= =?UTF-8?q?=20generate=20session=20ID=20if=20it=20is=20None=20to=20ensure?= =?UTF-8?q?=20a=20valid=20session=20ID=20is=20used=20=F0=9F=94=A7=20fix(en?= =?UTF-8?q?dpoints.py):=20add=20backend=20information=20to=20the=20Process?= =?UTF-8?q?Response=20to=20provide=20additional=20context=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(schemas.py):=20add=20backend=20field=20to=20the=20Proces?= =?UTF-8?q?sResponse=20schema=20to=20match=20the=20changes=20in=20the=20en?= =?UTF-8?q?dpoints.py=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 19 +++++++++++++++++-- src/backend/langflow/api/v1/schemas.py | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index b0fffebda..d47283839 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -7,7 +7,12 @@ from langflow.services.cache.utils import save_uploaded_file from langflow.services.database.models.flow import Flow from langflow.processing.process import process_graph_cached, process_tweaks from langflow.services.database.models.user.user import User -from langflow.services.getters import get_settings_service, get_task_service +from langflow.services.getters import ( + get_cache_service, + get_session_service, + get_settings_service, + get_task_service, +) from loguru import logger from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status import sqlalchemy as sa @@ -122,6 +127,11 @@ async def process_flow( session_id = result.session_id else: + if session_id is None: + # Generate a session ID + session_id = get_session_service().generate_key( + session_id=session_id, data_graph=graph_data + ) task_id, task = await task_service.launch_task( process_graph_cached_task if task_service.use_celery @@ -132,7 +142,12 @@ async def process_flow( session_id, ) task_result = task.status - return ProcessResponse(result=task_result, id=task_id, session_id=session_id) + return ProcessResponse( + result=task_result, + id=task_id, + session_id=session_id, + backend=str(type(task_service.backend)), + ) except sa.exc.StatementError as exc: # StatementError('(builtins.ValueError) badly formed hexadecimal UUID string') if "badly formed hexadecimal UUID string" in str(exc): diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index 835c9fcb0..8e48eb3c9 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -53,6 +53,7 @@ class ProcessResponse(BaseModel): result: Any id: Optional[str] = None session_id: Optional[str] = None + backend: str = None # TaskStatusResponse( From 5f3c602b29ca4a6f78ac6f0cfd4c1f16d3426f0b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:29:10 -0300 Subject: [PATCH 179/213] =?UTF-8?q?=F0=9F=94=A5=20chore(users.py):=20remov?= =?UTF-8?q?e=20unused=20test=20endpoint=20for=20adding=20a=20superuser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `add_super_user_for_testing_purposes_delete_me_before_merge_into_dev` endpoint was added for testing purposes and is no longer needed. It has been removed to clean up the codebase. --- src/backend/langflow/api/v1/users.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index e1e24d197..a216a2051 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -164,31 +164,3 @@ def delete_user( session.commit() return {"detail": "User deleted"} - - -# TODO: REMOVE - Just for testing purposes -@router.post("/super_user", response_model=User) -def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev( - session: Session = Depends(get_session), -) -> User: - """ - Add a superuser for testing purposes. - (This should be removed in production) - """ - new_user = User( - username="superuser", - password=get_password_hash("12345"), - is_active=True, - is_superuser=True, - last_login_at=None, - ) - - try: - session.add(new_user) - session.commit() - session.refresh(new_user) - except IntegrityError as e: - session.rollback() - raise HTTPException(status_code=400, detail="User exists") from e - - return new_user From c5a6003ef5dc4b06728a695be45867860cb99530 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:29:20 -0300 Subject: [PATCH 180/213] =?UTF-8?q?=F0=9F=94=A7=20fix(manager.py):=20repla?= =?UTF-8?q?ce=20logging=20module=20with=20loguru=20logger=20for=20consiste?= =?UTF-8?q?nt=20logging=20across=20the=20project=20=E2=9C=A8=20feat(manage?= =?UTF-8?q?r.py):=20add=20debug=20logs=20to=20check=20Celery=20availabilit?= =?UTF-8?q?y=20and=20backend=20being=20used=20=F0=9F=94=A7=20fix(manager.p?= =?UTF-8?q?y):=20move=20check=5Fcelery=5Favailability=20function=20definit?= =?UTF-8?q?ion=20above=20its=20usage=20to=20improve=20code=20readability?= =?UTF-8?q?=20=E2=9C=A8=20feat(manager.py):=20add=20debug=20logs=20to=20sh?= =?UTF-8?q?ow=20task=20launch=20details=20and=20backend=20being=20used?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/task/manager.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/backend/langflow/services/task/manager.py b/src/backend/langflow/services/task/manager.py index 4083978df..ab74d916e 100644 --- a/src/backend/langflow/services/task/manager.py +++ b/src/backend/langflow/services/task/manager.py @@ -1,19 +1,26 @@ from typing import Any, Callable, Coroutine, Union -import logging - +from loguru import logger from langflow.services.base import Service from langflow.services.task.backends.anyio import AnyIOBackend from langflow.services.task.backends.base import TaskBackend from langflow.services.task.utils import get_celery_worker_status -try: + +def check_celery_availability(): from langflow.worker import celery_app try: status = get_celery_worker_status(celery_app) + logger.debug(f"Celery status: {status}") except Exception as e: - logging.error(f"An error occurred: {e}") + logger.error(f"An error occurred: {e}") status = {"availability": None} + return status + + +try: + status = check_celery_availability() + USE_CELERY = status.get("availability") is not None except ImportError: USE_CELERY = False @@ -30,7 +37,9 @@ class TaskService(Service): if USE_CELERY: from langflow.services.task.backends.celery import CeleryBackend + logger.debug("Using Celery backend") return CeleryBackend() + logger.debug("Using AnyIO backend") return AnyIOBackend() # In your TaskService class @@ -51,6 +60,8 @@ class TaskService(Service): async def launch_task( self, task_func: Callable[..., Any], *args: Any, **kwargs: Any ) -> Any: + logger.debug(f"Launching task {task_func} with args {args} and kwargs {kwargs}") + logger.debug(f"Using backend {self.backend}") task = self.backend.launch_task(task_func, *args, **kwargs) return await task if isinstance(task, Coroutine) else task From a75e80b0f71fc3dea9500a3f4ea14deb540b256f Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:30:45 -0300 Subject: [PATCH 181/213] =?UTF-8?q?=F0=9F=94=A7=20refactor(endpoints.py):?= =?UTF-8?q?=20remove=20unused=20import=20of=20get=5Fcache=5Fservice=20to?= =?UTF-8?q?=20improve=20code=20cleanliness=20and=20maintainability=20?= =?UTF-8?q?=F0=9F=94=A7=20refactor(schemas.py):=20make=20the=20backend=20f?= =?UTF-8?q?ield=20in=20ProcessResponse=20schema=20optional=20to=20handle?= =?UTF-8?q?=20cases=20where=20backend=20is=20not=20provided=20=F0=9F=94=A7?= =?UTF-8?q?=20refactor(utils.py):=20add=20type=20hint=20ignore=20comment?= =?UTF-8?q?=20to=20import=20statement=20for=20Celery=20to=20avoid=20type?= =?UTF-8?q?=20checking=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 1 - src/backend/langflow/api/v1/schemas.py | 2 +- src/backend/langflow/services/task/utils.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index d47283839..e1cad2357 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -8,7 +8,6 @@ from langflow.services.database.models.flow import Flow from langflow.processing.process import process_graph_cached, process_tweaks from langflow.services.database.models.user.user import User from langflow.services.getters import ( - get_cache_service, get_session_service, get_settings_service, get_task_service, diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index 8e48eb3c9..519bc534e 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -53,7 +53,7 @@ class ProcessResponse(BaseModel): result: Any id: Optional[str] = None session_id: Optional[str] = None - backend: str = None + backend: Optional[str] = None # TaskStatusResponse( diff --git a/src/backend/langflow/services/task/utils.py b/src/backend/langflow/services/task/utils.py index e95fbdb33..5dfb03b83 100644 --- a/src/backend/langflow/services/task/utils.py +++ b/src/backend/langflow/services/task/utils.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: with contextlib.suppress(ImportError): - from celery import Celery + from celery import Celery # type: ignore def get_celery_worker_status(app: "Celery"): From 1571e8b9ee1dc3b853e0f21f889d1d55d008214e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:30:54 -0300 Subject: [PATCH 182/213] =?UTF-8?q?=F0=9F=94=A5=20refactor(locustfile.py):?= =?UTF-8?q?=20remove=20unused=20imports=20and=20variables=20to=20improve?= =?UTF-8?q?=20code=20readability=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/locust/locustfile.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/locust/locustfile.py b/tests/locust/locustfile.py index 259c580c8..000c3456d 100644 --- a/tests/locust/locustfile.py +++ b/tests/locust/locustfile.py @@ -1,9 +1,8 @@ -from locust import FastHttpUser, task, between, events +from locust import FastHttpUser, task, between import random import time import orjson from rich import print -import os import httpx from pathlib import Path From 419045f903ca4b4848a3e72fed94688be91d7da5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:35:37 -0300 Subject: [PATCH 183/213] =?UTF-8?q?=F0=9F=94=A7=20chore(ci.yml):=20add=20s?= =?UTF-8?q?tep=20to=20create=20.env=20file=20from=20secrets.ENV=5FFILE=20t?= =?UTF-8?q?o=20provide=20environment=20variables=20for=20the=20build=20and?= =?UTF-8?q?=20start=20services=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 017f57efe..2f6a563e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,12 @@ jobs: - name: Set up Docker run: docker --version && docker-compose --version + - name: "Create env file" + run: | + echo "${{ secrets.ENV_FILE }}" > .env + - name: Build and start services + working-directory: ./deploy run: docker compose up --exit-code-from tests tests queue celeryworker db continue-on-error: true From ae26a55c9f33864cdd07c9471246f29ead382733 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:36:53 -0300 Subject: [PATCH 184/213] =?UTF-8?q?=F0=9F=94=A7=20chore(ci.yml):=20change?= =?UTF-8?q?=20working=20directory=20to=20./deploy=20to=20create=20env=20fi?= =?UTF-8?q?le=20in=20the=20correct=20location?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f6a563e0..c390c974f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: run: docker --version && docker-compose --version - name: "Create env file" + working-directory: ./deploy run: | echo "${{ secrets.ENV_FILE }}" > .env From a2c5c1b71f708bac20b04f5189bc6140568126ef Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:37:56 -0300 Subject: [PATCH 185/213] =?UTF-8?q?=F0=9F=94=A7=20chore(ci.yml):=20update?= =?UTF-8?q?=20docker=20compose=20command=20to=20include=20result=5Fbackend?= =?UTF-8?q?=20and=20broker=20services=20for=20testing=20environment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c390c974f..6a84e224a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Build and start services working-directory: ./deploy - run: docker compose up --exit-code-from tests tests queue celeryworker db + run: docker compose up --exit-code-from tests tests result_backend broker celeryworker db continue-on-error: true - name: Stop services From f1b14232c62bf17a145add89818a3a4a1d876972 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:42:23 -0300 Subject: [PATCH 186/213] =?UTF-8?q?=F0=9F=90=9B=20fix(ci.yml):=20update=20?= =?UTF-8?q?docker=20compose=20command=20to=20include=20the=20correct=20fil?= =?UTF-8?q?e=20and=20build=20the=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a84e224a..0fba509b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Build and start services working-directory: ./deploy - run: docker compose up --exit-code-from tests tests result_backend broker celeryworker db + run: docker compose up -f docker-compose.yml --exit-code-from tests tests result_backend broker celeryworker db --build continue-on-error: true - name: Stop services From bc904fba69665dbaeb8b065635022eb6ccb6a5e8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:44:02 -0300 Subject: [PATCH 187/213] =?UTF-8?q?=F0=9F=90=9B=20fix(ci.yml):=20fix=20doc?= =?UTF-8?q?ker=20compose=20command=20to=20correctly=20specify=20the=20comp?= =?UTF-8?q?ose=20file=20before=20the=20up=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fba509b9..c8ffab2f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Build and start services working-directory: ./deploy - run: docker compose up -f docker-compose.yml --exit-code-from tests tests result_backend broker celeryworker db --build + run: docker compose -f docker-compose.yml up --exit-code-from tests tests result_backend broker celeryworker db --build continue-on-error: true - name: Stop services From d1203b7fb7fc3a05a9a9d0ab454910a677d0fb79 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sat, 23 Sep 2023 23:47:48 -0300 Subject: [PATCH 188/213] =?UTF-8?q?=F0=9F=90=9B=20fix(ci.yml):=20remove=20?= =?UTF-8?q?unnecessary=20working-directory=20from=20docker=20compose=20com?= =?UTF-8?q?mand=20to=20fix=20build=20and=20start=20services=20step=20in=20?= =?UTF-8?q?CI=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8ffab2f2..d81146d19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Build and start services working-directory: ./deploy - run: docker compose -f docker-compose.yml up --exit-code-from tests tests result_backend broker celeryworker db --build + run: docker compose up --exit-code-from tests tests result_backend broker celeryworker db --build continue-on-error: true - name: Stop services From d8ba8f07468f5219b91dd71b6546d61be8cf4ab7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 24 Sep 2023 00:09:53 -0300 Subject: [PATCH 189/213] =?UTF-8?q?=F0=9F=9A=80=20feat(endpoints.py):=20ad?= =?UTF-8?q?d=20warning=20message=20for=20experimental=20feature=20to=20inf?= =?UTF-8?q?orm=20users=20about=20potential=20issues=20and=20encourage=20re?= =?UTF-8?q?porting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index e1cad2357..0ac6cccb9 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -126,6 +126,10 @@ async def process_flow( session_id = result.session_id else: + logger.warning( + "This is an experimental feature and may not work as expected." + "Please report any issues to our GitHub repository." + ) if session_id is None: # Generate a session ID session_id = get_session_service().generate_key( From a5da416bc4840ed094bd1073a9b617b28bf75197 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 15:54:55 -0300 Subject: [PATCH 190/213] =?UTF-8?q?=F0=9F=94=A7=20fix(users.py):=20add=20s?= =?UTF-8?q?upport=20for=20NEW=5FUSER=5FIS=5FACTIVE=20setting=20to=20determ?= =?UTF-8?q?ine=20if=20new=20users=20should=20be=20active=20by=20default=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(cache/manager.py):=20add=20warning=20log=20m?= =?UTF-8?q?essage=20to=20inform=20users=20that=20RedisCache=20is=20experim?= =?UTF-8?q?ental=20and=20may=20not=20work=20as=20expected=20=F0=9F=94=A7?= =?UTF-8?q?=20fix(settings/auth.py):=20add=20NEW=5FUSER=5FIS=5FACTIVE=20se?= =?UTF-8?q?tting=20to=20determine=20if=20new=20users=20should=20be=20activ?= =?UTF-8?q?e=20by=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 5 +++-- src/backend/langflow/services/cache/manager.py | 7 ++++++- src/backend/langflow/services/settings/auth.py | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index a216a2051..ed85e148a 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -13,7 +13,7 @@ from sqlalchemy.exc import IntegrityError from sqlmodel import Session, select from fastapi import APIRouter, Depends, HTTPException -from langflow.services.getters import get_session +from langflow.services.getters import get_session, get_settings_service from langflow.services.auth.utils import ( get_current_active_superuser, get_current_active_user, @@ -32,6 +32,7 @@ router = APIRouter(tags=["Users"], prefix="/users") def add_user( user: UserCreate, session: Session = Depends(get_session), + settings_service=Depends(get_settings_service), ) -> User: """ Add a new user to the database. @@ -39,7 +40,7 @@ def add_user( new_user = User.from_orm(user) try: new_user.password = get_password_hash(user.password) - + new_user.is_active = settings_service.settings.NEW_USER_IS_ACTIVE session.add(new_user) session.commit() session.refresh(new_user) diff --git a/src/backend/langflow/services/cache/manager.py b/src/backend/langflow/services/cache/manager.py index b73908e51..19f5034cf 100644 --- a/src/backend/langflow/services/cache/manager.py +++ b/src/backend/langflow/services/cache/manager.py @@ -7,6 +7,8 @@ from langflow.services.cache.base import BaseCacheService import pickle +from loguru import logger + class InMemoryCache(BaseCacheService, Service): @@ -221,7 +223,10 @@ class RedisCache(BaseCacheService, Service): "RedisCache requires the redis-py package." " Please install Langflow with the deploy extra: pip install langflow[deploy]" ) from exc - + logger.warning( + "RedisCache is an experimental feature and may not work as expected." + " Please report any issues to our GitHub repository." + ) self._client = redis.StrictRedis(host=host, port=port, db=db) self.expiration_time = expiration_time diff --git a/src/backend/langflow/services/settings/auth.py b/src/backend/langflow/services/settings/auth.py index b6d288183..4a3eee100 100644 --- a/src/backend/langflow/services/settings/auth.py +++ b/src/backend/langflow/services/settings/auth.py @@ -35,6 +35,7 @@ class AuthSettings(BaseSettings): # If AUTO_LOGIN = True # > The application does not request login and logs in automatically as a super user. AUTO_LOGIN: bool = False + NEW_USER_IS_ACTIVE: bool = False SUPERUSER: str = DEFAULT_SUPERUSER SUPERUSER_PASSWORD: str = DEFAULT_SUPERUSER_PASSWORD From be85fcc60708b0ca9a61045e630de317acb76656 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 15:55:10 -0300 Subject: [PATCH 191/213] =?UTF-8?q?=F0=9F=94=A7=20chore(.env.example):=20a?= =?UTF-8?q?dd=20configuration=20for=20new=20user=20and=20update=20OpenAI?= =?UTF-8?q?=20API=20key=20=F0=9F=94=A5=20refactor(docker-compose.celery.ym?= =?UTF-8?q?l):=20remove=20unused=20celery=20service=20configuration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/.env.example | 7 ++- docker-compose.celery.yml | 103 -------------------------------------- 2 files changed, 6 insertions(+), 104 deletions(-) delete mode 100644 docker-compose.celery.yml diff --git a/deploy/.env.example b/deploy/.env.example index 1dc7904e9..e67307406 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -46,7 +46,12 @@ BACKEND_URL=http://backend:7860 PGADMIN_DEFAULT_EMAIL=admin@admin.com PGADMIN_DEFAULT_PASSWORD=admin +# OpenAI configuration (for testing purposes) OPENAI_API_KEY=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 +# Superuser configuration LANGFLOW_SUPERUSER=superuser -LANGFLOW_SUPERUSER_PASSWORD=superuser \ No newline at end of file +LANGFLOW_SUPERUSER_PASSWORD=superuser + +# New user configuration +LANGFLOW_NEW_USER_IS_ACTIVE=False \ No newline at end of file diff --git a/docker-compose.celery.yml b/docker-compose.celery.yml deleted file mode 100644 index 88e3cc189..000000000 --- a/docker-compose.celery.yml +++ /dev/null @@ -1,103 +0,0 @@ -version: "3" - -services: - backend: - build: - context: ./ - dockerfile: base.Dockerfile - depends_on: - - queue - - db - environment: - - LANGFLOW_CACHE_TYPE=redis - - LANGFLOW_REDIS_HOST=queue - - LANGFLOW_REDIS_PORT=6379 - - LANGFLOW_REDIS_DB=0 - - LANGFLOW_REDIS_EXPIRE=3600 - - LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@db:5432/langflow - ports: - - "7860:7860" - volumes: - - ./:/app - command: bash -c "uvicorn --factory langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level debug" - - db: - image: postgres:15.4 - environment: - - POSTGRES_USER=langflow - - POSTGRES_PASSWORD=langflow - - POSTGRES_DB=langflow - ports: - - "5432:5432" - - pgadmin: - image: dpage/pgadmin4 - environment: - PGADMIN_DEFAULT_EMAIL: admin@admin.com - PGADMIN_DEFAULT_PASSWORD: admin - ports: - - "5050:80" - depends_on: - - db - volumes: - - pgadmin:/var/lib/pgadmin - - queue: - image: redis:latest - ports: - - "6379:6379" - - celeryworker: - depends_on: - - queue - environment: - - LANGFLOW_CACHE_TYPE=redis - - LANGFLOW_REDIS_HOST=queue - - LANGFLOW_REDIS_PORT=6379 - - LANGFLOW_REDIS_DB=0 - - LANGFLOW_REDIS_EXPIRE=3600 - - BROKER_URL=redis://queue:6379/0 - - RESULT_BACKEND=redis://queue:6379/0 - - C_FORCE_ROOT=true # ! Only for development - - build: - context: ./ - dockerfile: base.Dockerfile - command: celery -A langflow.worker.celery_app worker --loglevel=DEBUG - - flower: - networks: - - default - depends_on: - - queue - build: - context: ./ - dockerfile: base.Dockerfile - environment: - - LANGFLOW_CACHE_TYPE=redis - - LANGFLOW_REDIS_HOST=queue - - LANGFLOW_REDIS_PORT=6379 - - LANGFLOW_REDIS_DB=0 - - LANGFLOW_REDIS_EXPIRE=3600 - command: celery -A langflow.worker.celery_app --broker=redis://queue:6379/0 flower --port=5555 - ports: - - "5555:5555" - - frontend: - build: - context: ./src/frontend - dockerfile: ./dev.Dockerfile - args: - - BACKEND_URL=http://backend:7860 - - depends_on: - - backend - environment: - - VITE_PROXY_TARGET=http://backend:7860 - ports: - - "3000:3000" - volumes: - - ./src/frontend/public:/home/node/app/public - - ./src/frontend/src:/home/node/app/src - - ./src/frontend/package.json:/home/node/app/package.json - restart: on-failure From a2c7d79451a28cba376b5562541e175913350bb5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 15:55:33 -0300 Subject: [PATCH 192/213] =?UTF-8?q?=F0=9F=93=9D=20docs(login.mdx):=20add?= =?UTF-8?q?=20guide=20for=20sign=20up=20and=20sign=20in=20functionality=20?= =?UTF-8?q?in=20Langflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📝 docs(login.mdx): explain the purpose of login functionality and introduce enhanced login mechanism in Langflow 📝 docs(login.mdx): document the crucial environment variables for configuring the login settings in Langflow 📝 docs(login.mdx): provide information on how to disable automatic login and enforce user authentication in Langflow 📝 docs(login.mdx): explain the usage of LANGFLOW_SUPERUSER and LANGFLOW_SUPERUSER_PASSWORD environment variables for setting up a superuser in Langflow 📝 docs(login.mdx): document the usage of LANGFLOW_SECRET_KEY environment variable for encrypting the superuser's password in Langflow 📝 docs(login.mdx): explain the purpose of LANGFLOW_NEW_USER_IS_ACTIVE environment variable for automatically activating new users in Langflow 📝 docs(login.mdx): provide information on the command-line interface for managing superusers in Langflow 📝 docs(login.mdx): document the sign-up process in Langflow and provide an image of the sign-up page 📝 docs(login.mdx): explain how users can change their profile settings in Langflow and provide an image of the profile settings page 📝 docs(login.mdx): explain how the superuser can access the admin page in Langflow and provide an image of the admin page 📝 docs(superuser.mdx): add guide for superuser permissions in Langflow --- docs/docs/guides/login.mdx | 122 +++++++++++++++++++++++++++ docs/docs/guides/superuser.mdx | 7 ++ docs/sidebars.js | 1 + docs/static/img/admin-page.png | Bin 0 -> 175531 bytes docs/static/img/my-account.png | Bin 0 -> 32831 bytes docs/static/img/profile-settings.png | Bin 0 -> 349196 bytes docs/static/img/sign-up.png | Bin 0 -> 68595 bytes 7 files changed, 130 insertions(+) create mode 100644 docs/docs/guides/login.mdx create mode 100644 docs/docs/guides/superuser.mdx create mode 100644 docs/static/img/admin-page.png create mode 100644 docs/static/img/my-account.png create mode 100644 docs/static/img/profile-settings.png create mode 100644 docs/static/img/sign-up.png diff --git a/docs/docs/guides/login.mdx b/docs/docs/guides/login.mdx new file mode 100644 index 000000000..a2d7c5502 --- /dev/null +++ b/docs/docs/guides/login.mdx @@ -0,0 +1,122 @@ +import ThemedImage from "@theme/ThemedImage"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import ZoomableImage from "/src/theme/ZoomableImage.js"; +import ReactPlayer from "react-player"; +import Admonition from "@theme/Admonition"; + +# Sign up and Sign in + +## Introduction + +The login functionality in Langflow serves to authenticate users and protect sensitive routes in the application. Starting from version 0.5, Langflow introduces an enhanced login mechanism that is governed by a few environment variables. This allows new secure features. + +## Environment Variables + +The following environment variables are crucial in configuring the login settings: + +- _`LANGFLOW_AUTO_LOGIN`_: Determines whether Langflow should automatically log users in. Default is `True`. +- _`LANGFLOW_SUPERUSER`_: The username of the superuser. +- _`LANGFLOW_SUPERUSER_PASSWORD`_: The password for the superuser. +- _`LANGFLOW_SECRET_KEY`_: A key used for encrypting the superuser's password. +- _`LANGFLOW_NEW_USER_IS_ACTIVE`_: Determines whether new users are automatically activated. Default is `False`. + + + It is critical not to expose these environment variables in your code + repository. Always set them securely in your deployment environment, for + example, using Docker secrets, Kubernetes ConfigMaps/Secrets, or dedicated + secure environment configuration systems like AWS Secrets Manager. + + +### _`LANGFLOW_AUTO_LOGIN`_ + +By default, this variable is set to `True`. When enabled (`True`), Langflow operates as it did in versions prior to 0.5—automatic login without requiring explicit user authentication. + +To disable automatic login and enforce user authentication: + +```bash +export LANGFLOW_AUTO_LOGIN=False +``` + +### _`LANGFLOW_SUPERUSER`_ and _`LANGFLOW_SUPERUSER_PASSWORD`_ + +These environment variables are only relevant when `LANGFLOW_AUTO_LOGIN` is set to `False`. They specify the username and password for the superuser, which is essential for administrative tasks. + +To create a superuser manually: + +```bash +export LANGFLOW_SUPERUSER=admin +export LANGFLOW_SUPERUSER_PASSWORD=securepassword +``` + +You can also use the CLI command `langflow superuser` to set up a superuser interactively. + +### _`LANGFLOW_SECRET_KEY`_ + +This environment variable holds a secret key used for encrypting the superuser's password. Make sure to set this to a secure, randomly generated string. + +```bash +export LANGFLOW_SECRET_KEY=randomly_generated_secure_key +``` + +### _`LANGFLOW_NEW_USER_IS_ACTIVE`_ + +By default, this variable is set to `False`. When enabled (`True`), new users are automatically activated and can log in without requiring explicit activation by the superuser. + +## Command-Line Interface + +Langflow provides a command-line utility for managing superusers: + +```bash +langflow superuser +``` + +This command prompts you to enter the username and password for the superuser, unless they are already set using environment variables. + +## Sign-up + +With _`LANGFLOW_AUTO_LOGIN`_ set to _`False`_, Langflow requires users to sign up before they can log in. The sign-up page is the default landing page when a user visits Langflow for the first time. + + + +## Profile settings + +Users can change their profile settings by clicking on the profile icon in the top right corner of the application. This opens a dropdown menu with the following options: + +- **Admin Page**: Opens the admin page, which is only accessible to the superuser. +- **Profile Settings**: Opens the profile settings page. +- **Sign Out**: Logs the user out. + + + +By clicking on **Profile Settings**, the user is taken to the profile settings page, where they can change their password and their profile picture. + + + +By clicking on **Admin Page**, the superuser is taken to the admin page, where they can manage users and groups. + + diff --git a/docs/docs/guides/superuser.mdx b/docs/docs/guides/superuser.mdx new file mode 100644 index 000000000..04e0f96af --- /dev/null +++ b/docs/docs/guides/superuser.mdx @@ -0,0 +1,7 @@ +import ThemedImage from "@theme/ThemedImage"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import ZoomableImage from "/src/theme/ZoomableImage.js"; +import ReactPlayer from "react-player"; + +Now, we need to explain what are the permissions the superuser gets. Once logged in, they can activate new users, +edit them, diff --git a/docs/sidebars.js b/docs/sidebars.js index bc02f7407..4624190ba 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -52,6 +52,7 @@ module.exports = { label: "Step-by-Step Guides", collapsed: false, items: [ + "guides/login", "guides/loading_document", "guides/chatprompttemplate_guide", "guides/langfuse_integration", diff --git a/docs/static/img/admin-page.png b/docs/static/img/admin-page.png new file mode 100644 index 0000000000000000000000000000000000000000..aa23164deaba9ecb05f35e2decd7b8640addf568 GIT binary patch literal 175531 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�((`R91PY@PB$t+S{NFnws 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
+{ELf@$bLEWNP_pAI)?pnXUYXipTLcdYhlR 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 z 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%0AqQ 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ΞO^u9~CdYN*CrR!iynvj4B zPLpH+v_tZ&;%dyvz1bSOBoqpsd|Bz?XdN@T_S_i2#vz^WY_m(4kn5e`bHNqfj2NdL z5BgVD(Vuk61A{Xm!-FKcD-fmgK5_ymbacYF!QFho7HgPrv&8#!WHPW%tj5fr!)-5u zE)G68w|>0ix|(ppHfRp;TPlu|NkS4$EU!YTpex~GSnNArJ`*A*ZOXp8lrcN3zf>9% zngi+On!EDQTFK37Gz0^ci-cD@}25(7J-lJl5s7>EiU^MFu9vt*}C~ z?6#9(+%=kWy`#Q%QnI=UOBgEPr5Z|t+$UrCH4k`m_~7=-Nr8cm`agkdMpq%sC~Wsv z0pf$Yo(ETKVN~~=x;TM0~(%+-eg@TWq@X_H$B1PzVwZuIZFq4lO+k zy(KVt?*I;z8d@;vOD%mS{_bBF`s(OP@08bdx!toZG@WJGKfub_XnRPP3%L9SLPa2C zJI^8P@`x_3gzdcE1+7#_Hlix-WIu00Rq)yxguhMXZq^nWN5z#pIHX+0I6mMT!mro& zNdaIE&96!B*&i(+<)M~x*H4J=ubj9sI!*Q{2Ny?X$OHdjD&h=#t=aXYz_SJ7=mWa3 zj(s6~>rNU*+Zoq~Nc)p{g5Ba}Tg>ErTos7(J)L9eyjcB4(~Zl?1bS|L;-$PS3CSdsWNk3QD+wP6HBku??;|>cjLiq@M3{Y6F|i4v@FxdtbWoxHpzHE zrkw_`t}>n8=vcan>@MJVMfeq!v9LO*5e!S3iou>vt2nyuIX^x6D&3{)CC;X}LLqd% zUXT$jrs3|@qPRU6W9ULmvA?%bb6l)l>ozX5c99Z&#dQ5FyRzkgg8zRt-Mn$#Okv@c zppi=IlhbLpdPRpFHX$Kap1{CSJr0twLVF{4QbJ;vPlf;KYm|(B+(Bz`OFO{h;QDfp zr1Zm<<}Wz;6Y{4a=N;a(XZv$!`sh1wMs8l|wTzSDQk+LGnk#75sI|w#>{_V{?Q{@4 z^Y;Ifeuk>#{bL|Ac`V>n^up{|<>?-PIacD4I;O;UH-drrl3~fv85z1&jM;H+_VhVA zcXfMcQ)6dRJLsqu5*FiGduhe+9g63HEK!x1)Gz>r1iEEdA+MKg#)tA+MF_sE)D5*{ z0-;!~Gzkv>MyA?O9*D1c;=88B;ZoniyJyky%3ELGhO>8As>c(%!Tw zXZ3|nz}ix6E*rKfnoTd=Knv&0P^UFJagdi#8f^(n&sv)Zsw#pZLFRxO1Yv<3~mX1Q_13sXgKCkt> zJV-V;P$&={rd^%NCktvmZ9MZyAsb^WtVYTxpcY=iCuiAuW9 z$1tbGc!w|B`^T!RVFJ)<_k&tEtatTGfMtuwiyN~JTyzl<%9`hqi9q+el<+szH1O^e zXh~fna@nX4xtcIMeWc3;vtiR=xnB<5*zKG(O>O!KtC~a7-oHveXqwKi>;Xbtl!*Qy z04PIcsO5x0Qg^3VrH|Lmw0*}awVCczH%biqE@^ctU=jr<*}8F;h&4~&W?c6LDsz%Z zw=cW=ZA3daljY!|<7yq{*J~Gzr2nGk1mBjvqgdSXKxko^MJ9`RZb)^~_sQ~VZ4@oJ zAqOYwKJ3&VIp*tLfBj#vm$Jg_arOVYxQn=QagB=5RiI1HMu|5XdJWw}lfDLC-;=@y zIq6CB%uROv@4vNP12qn(7X#g`PbtYsL+tSqk*EQd>xz|8VBnu9#~4(`%D`1XT^6bu zY!nnD@^+yPp;SS;+Hs>?44@@sG>(08tGmI6O;^;fgydq(_(#d~6e-aM!hW=Fz`_0f zRh@&3wmV46k}Wu&!m<)xdkmjBX0JW#=bWc*ib6N-Lb+y30qqcu@nuu#bB6y_Z@;&^ zI!wp^)sS`&KkzG25W%FC)r1uu-XE)ui#aw}U{PE<#2J#_C-`EgiGC=@4?7;5hak92 zR_?h9`KXs3Tt>KTHfv3sg%Ik^Iu8S%o=mTvhz^$nf3z66KF8*o#1vBY?7XFMY5?gV zUoW-APvCGb{4w!SoJENoBVRV}aZEUbswRE|)hJCmly_h!1kNuH&~)>XMzW))raP?m zle3y%2DBPYM#G-)@EfPV*9Pt9x^3{}sYVooQl{qoEo~T2h0+aY6Qnr;(ch%HphEJ<9Fl{59iBYcdxjBx*iD+=;ZmC@Fh6@D$`Tr18+U=Rz9nA*W9}@yJ)q zqo9Zb{2wRFbXdzC%56E(1(kW_!i9hrf5RD&$dt!?klC{Y=lkAbeM~8rot?AGgv>c#Y@r8=B*;&T*b{qhA{T z)*(plY)4|?8r-Ht3#7R*n{RbE)v|gtlTM0WikPl69XE(k+i```NKE(DRwm)xC4~;) zQd6dPUfgq0T>YBaKbE1!3r3`H+~sF7ppiiC=H5)9?}e04t6KqM85g{$kaf4e$}I-G zRZg=Yq-J;`C3JpzOdT(O^C`^YhcauHi)JM&o2=;v#c&#l)xuVPIXLUW2=0mZ6|L|d ze-m04tK3)4W-;~$O@VcO#b8i#!ngyM#a2mo_`KN;=y{R&D{a#l$sx#%x3IKwJg|dp zQmk7qPWCCC&P5N8jNmOBWmn(DmC@bvJPv?(;9E0)k_;p;~A7#wj~h}MQfyzG!tz6^$^5D5H9K>eq>&PGEi`$<}jpNu2h_+NdC~@A!x3N*fpE`5M^g z*$>hs=j zPWyD)@H$xQzXoh`zq9m$TS}9@P|*v(Wtq|Vq=ABe-9yUWhm}bdcrsmwHHQNB${@j0 zRpeWAsP_Yu@R9<%EsfY`Mfec!VOy!Avl1$7ln(j<0DAkCjiiw1s`#)Semh55-=_u5 zo5)$rg#H>l{!~U{#8u1a*{jaU(7I*p>_%MhGr9@d)!NwCd_L*n@$|J3dxQ>v$QX*+< z_dS4kd%>@Z^5*%k?*{^|&Kl41>1f}WR8wl0!oh2pe|-bOo^0_Cuw}lnvqun{$}z64yhm*92H@#j9S&TBtw4TRJm>n(Krbh- z#4Mp?#65*JFAhquDAftPcmr@VR!FS+PRsDFu+LWcOEG&H1;tFkET4MUAUl(Q;#;^R zWAa8#rmbA084D1+6_moGo;xMU{0OD~zzQuJEAF%REj~`L!o!OFf=94oUDU!UytZ|( zJw0wi2%ZihaLZi8U+3ea{xts@v5WFdn%2lQMiFY@8!l&BGTodcqEy@um%dANWfaHy z8(714gF|>GRlwLORJC?UOz$FldwtS^|4@Je%YPUV>Z;bC%>D`i2&g1HSgS0>-iWK) zpsR&mgTs)ouh+P1*rhQqtENSF_B39YndTvnzlBuU3zCQVaFlL_k(nf->Zb>I~7+f zk5%=U{4oT(v3;wKA$&@RV%*K7O<>>xl_zv|gxB&-Nmr}sf^jLlz*c_lqQG>0}_{sjfo z&&bjo<=1x()#nuU5Z{z9K1pCJdMZ8x(GeNR^@TNKdW6T(F3FsVU5|2q$x(jEiLc(i z8I;-viKK%#_)CYefe09+C+jB!=EF~wFmj>4P9^v^_oc%yi_DTQF#PmfV6ERa1o`Df zIAiv`w!{~=pAURIxz|o}uU#s3c<+OgDDxW``q_2dK5(XS0wmMmWaQFUSHrb**mJhB z7f0;kW6sMY@ItV^Yah+B{Zt#TsnTp29~!n<)&1NqQK|GS(0_>wmD~wYSK@VfD=$8n zZ?XxC=l;fg^YHJqVNl4epS%BkJ9I3S5%|L;-~M152M%xxII_hPz!0d& z(zJ4mt%v`3bUEbNm`eaB{BG)1cNEVij1MM(i83#(sV~JWe!2P;9F?a->Yce z&?hLtjFU+6w@Z(cwK5#NZwIlD?tG+0-6=YkN}{@RpTk&+#?s#7pm+FW1&M!s?fUKU zxQiD0Mr5;Vj?x15v>LOp*G@+$S1rZun1L53x@{aD`duAs+D*OOzok82m{49NE?pk2 zg>OTgCw*bt9YKK^a8?|oHh7V%o>J*3fqLXvXFr<{gS_aZle^kHxN@!?gJv$b;{JLA z>tQj&HLG6|W@5c>Y{d1bW$Re4a&g;5IIZ@8GU8jH4 z-;58`%|;5I{@_zAO0T9lvc7niIkVM~(WV5bAFuq4PAtNJiJ5gC-$brkCJpERExlRO z3t#rU&%8X6_BkiVE;oeE>DVKX?7L6Iut>j_#J!!y{&Zk7t-N^%+T!1Vj;q7;=-&&( zPtPRT^$fKde+MZ<&oA~)cFbwQ-oa_pO7H>;;Ag$yWu;CpoE@=pwMF_}eS-6xuLc%~ zMjatHH36tr1q)VG&U;3W*{Q!V7=)c`npQY=BTzqSHvF)FW0%E~NyFj88~z`xLUqXJ%lW)$ zr~{P6S$DI2M!J*9qMG}TJ#YEv=jP0bKXY2s1g*->eVv5*hvkhbf$=TLVkVRI)HS?Z zZzh0&FH_zc$QRuK|lsGH==Dm*PK~3K!we6}|jN{i-j-cxTj4=^an7Pufj~XjQGxgQsndg<_`};Fk7VhI4`|TT$Rk%1rRy zxJUy$5WQdNWS$bEAuQ|^`<2%Ht8`u2WBIIEfV(x?L`z;*TdR1bP}a@TK}>^*-_ z-}v3!?#jDKf!t1qrftO$l|P~g`{6&O1Q(r2Cm#~Hvb=yGR-=NkX=@Wr6Q+D0TtG#4 zhU4nUG@Mg+!J8Yd(T^sCxFfr3^W8*iU1SQYd-&vgkards3nPQ&_3(@wg znYfLA6is=L|L0wn+1PY_iOfHlwDZ4O3K<-BnQypWJ?TNNsqAAFsbqFGUIbGqBO}x~ zH6FrcSNGcbzDpE-*_h^?_j+#rhh1EToJK%e_e3sYG>Ws3C4#y9)a(25(?%AKR5pTw z#Pr76hx=m^#+vsi)e(KT`+BdbkF_rE2e~fhzYSF>ucopLbp_Kr8q4cc8?7$FK#yGa z$EdO}`>)P#$8@KDWFxO>uo$b@!qFk5Lb4phz$-Ez@22h3Hf#oEBRhLFu^*tT^u`s_ z?GIm!7YgBcN@`9eB8SqAd@*GEVX?8pfnbW^9g zexSin)xWvekW(whBhgGF`z;msUq+J`t^N<$#tTpcmQ#iKZy4>Ay}m0h>{yzs#iumx zp2lL{;JOP8qT_J^_(F5Mm`O`h*d6OF}P>vwD0n;Q6IoLPfn5TeB1IOB;cPqKk5V&*F>YrM!y zUspY*>{IWcL?4doXWh8Lg!t&n`U=4lg;_pYuC{O3}3LBJoy67$HDyumn{*yP_+=AD%O4R~f@qh@7^6`$eyh`Zbio z&Nrv;uC^m8cugip2@{=(VMU2VCHGSoZ=B#sm$WKqaLUfY)^+F1On2xs=S{|QoV*fj zb@IKX`A3Bi<{Yld{{j=SjBL(rK;Hz)w&Zv@c1ANf9^efzt%sU5uY~VMF0AIg$*w%| zHGYBZR=Bz?47OYLfpd z&Km>9NDwG z?G#XDz4fC<{DbP4-0#J7vWEQ;L4;+n6*p@Db+$?)s|81J(2s!+$4+t_Z@(;6ziwP- zrrmYkejJq-i$!^%!8iF+xL$!DnRn6yrpL-TOR?ybCb8z+`$yGcf9>wZ3>?qXHeJ-A z@|^QcP9E$~4niJk^tSB!Maxe&&P#NKXD)X_!)yPP`iNcMi>qf$yzc%Y_f`vWEOB5- z$5?ZC6iUOViQT(m@wWZPzhG0e(6KO&na3dj>csF@>pi!Oh-LB$xa@NQp}mA@!0DTB zCtsd+zvps62EoLAj6_W9?#c9`V|ijRPwk50(uIGi3Xin7DqYUZcq_KNjm72xyYJSt z9~_uw5Xg42GcALx%>vH3zI*kDs22$NpLB}Pdt1J`)aCDv)7a!+SeIys3j%fScG6w4 zYFkufRXtF;lroy;?2(8qIgHemT3&Uw>@(H-62eKI2M$h)Q z-)y{VeK5}_ztMbKZ?G#^HCWiPT1J_B&Y&^CRwNT` z%%CNbN18T(tlKeATz%lG>BG+=&h31OMU_F*1;V*$%O0@xBXi$aSyilCxt;gdH=pwq z9LGvEF?X|f-|`H}^VDt%=qH#YuizUD3sZbaU~wK%${cdg;FqO%j5(!2EUZqbcQrBd8nD`sZ( zlmCm-EDFvjejak$b;gpC@`Vmr0yT2_FP&iTTUi0yuA_7Lhan~PDb3GqbLoAXBk5yG zB>xHqobN6sI~vODgCpqVzTB)-WauHZPZ->R`TlB6F@E?e64c3@gM9dREN>}7fg9qV z5t%8`mV#`x2$=d5_oKL5gEM`((S#{zK}V^}Qb9X!@5Ni+hZdumGoQnLvGR&1Cd0IS znEFy*7$~l^nKU+E12bt$mj@^rc6!J9#VHNOgVNO0(!c10l9rO6xa|8HjoP8dsa0uE zxpDtS{E}sgJjo9=h38Ipal4#O#10$p{khJQ)$_|Ri&lWRO1W_KMgD z7}OBXcIw1F@qnPL7M|0+Q>NbVRvW?dsdV>zbv|jYP|P&K?d<*G=X}1+d9g0Gt*_S8 z3GB~G5ll^+KRp_7ur4gsR-4S6A&};IIG6B)Dlplz#d@>1N+9QV%DOe*W5DRf<554} zdA(-H6ICdl=4dl99fY1HE!StS0I{dP$#B^dYXNmZuWOczwB5d+D#_Ufg=f$EJdf}(8UkpjV}5+d~O@pWcfa8T8VXAf5smr zpvkm--x*wgeF`O>SC~X?k>8TYn_@b;D*I}Ak_Gbl;I~a`>6~3f+-@DGWLy@KbPWNl z^;%rT+YDjZYcu?}HIx3H{voiWAd1V29#wBnMKQVPhqHVsi(^@i<`IcybFOjy!|={k zvGKD2i3r&%UYRK2^x4JTDJ2?BmOb5KN9H~Qh)#$K*Cf6ZPChpeE7ON}sd1samDf!H zU{VY8>&DZtTg@@eJ)WFDsNqQ=7Iqn{4eQFtU@CZ32A?`j)o1iY`zHvGLzBHQT>J*c zAz8>6L%7aV^*PQVIazc8OgfE4^EzS_%6=cVzwdaT;J@eL5w*sXU(V|_8sR&NS0hS) zWq8CvWl^OvSBtMN{|6Mmwb?=bXD#~SMf^>jr~hSW0iQ`Rl}9(Kth2qu+8@!o=f7r| zW;!pFJ~eKrz7_%)H+2bR(gbFqHB3YBb4}Y?i!u`9Hn!eNVLh_6Zb$pr_b+jA;15AZ znof{ZvT~8~bD&7rbX7v zGwAek9ekhb0VkDgE<^2We^2du1?@on8B0X|6_lSU3-x=Fg{k|C`y9ct6LYI=2X06?%E4E zG9#TX4on-wuWw*=<`nKKYJkkVli^rD7)V1o%h>p_j8A8LECZNy_mr)U2KY}0f6C~k zqb3&ig;5K`>MY0H$Q9jer~h7d`IvTm=g@CpNxWi9+n~rT=enyPRq7hA#D0$D#UM#> zJ*1=t1pu9M&}>u=&QI1aS5ig=p}8`_Mi1S@7KB9WY!5@pnVH#i_i$BnznjW7yYiAb z{8C`rMWALMGIgKmIIty43`5M8@Q)lVTMpHuv2J?<&R0ui6TI3wHjVIj6^N>6nZUsG;K6 z;GIPwp?@>Gd5iI4MK77nMvNX=(cgWIjI=5diC$BA`fGD6t(MMQHDe>2TGYGI(@DNd zv#ir?b@BR7+LG#lZ8LlX=-&}23WK^`GG|`rT~O%R0X-uYc|J2lDhg`X77c8<7sek#&8}=t3 zoTn2vx`(xB%ez(xT9cT-Crth2OMZj773#I^Vslb<4mQ(CPw0}!b0s=s@c*d%{r+W3S45Jp;71;4ktBL z8Wz2im>ZY!aw5a>_gczKI@|l1)S(^Qjt&-3)k;ARX=1p>=D_w60BStTatg{ROH%!H z{8nEC==C&Q>dVmZn>l3xMlSiqVerQQc$mlP0(~xm%CBpsNXbbe7d@h-lU8v?j><@1 zzfH@QYgR$Yu$*??CLJtX0353XBf%x_>zP z&c-!S_FQaN>dqJ4q29G~o$0d_boPxMY@H;!5)rFZ+eovU?hDQV%#wVf6UJE+=o#o4 z4hLxTbFqRhYu!Hnb`v~&!JS~_{z~-u!8RKHq?NG2{^`%|JKeK>08EcmXj$b;Ay1)3i+2(`WQlQ*b zYSEv2CcHPtef(2dDs$3LtQoD^#j;%3_5(&Y{4*@+VkW1&C*Q_^3tCl^#=NS^-Ew++_{tyvx9em5R-@$N z+=pQuWKnEXD{0dd%>e89hcY!GRySSQSrXMEd%^NF8GZeXzh-~-r94JM9)oh- z=h?6xYTZ_&OU`UV25rwz)W<58jP_rxV~ovR%9@8B7nZI45Tbp_SZK6mPZPqx3-j58 zw=|NDiRgtTKxT8x3$Bv3c7xMIxsQ7fzu0QBT=Qw29Z2BFlb6K_+Y!gSV2a>d3BG~P zDjDQQM&93dGfQnR*cX=&FA?@&*L!O&-oG4`zgK8O_A<7*La%0|TEJ|dY3k%AqvFiq z!ZhKWq^EQaDi*ulWF7jtJ{Z59C=;>{_X5`Y8|}ndaK!1xC4Xc^ODgV zSV$^bT28w@bOhb!AKdNHPkJ(x8!V62h+T||m75U)PZ6R9^I~@fcqjUAPG(3{9zQoD zN#qn4YpVyJifdt;Ea_ua9q($o8e|qna7(y!V>bid=YzHH$G<#^T-yN`?dGvj@m_zR z8=w7KtEF2d@j8jyMSwQ#5&X@>R8q4^`)XK6N#Z5CvTJ2{^y)C}g?x3sL|5pa#Q>H^ z5}H$?x*<=B1u=QsV=I!87Z-7?$< zyL3?{4s4}ilMM#bh3mTpF<^GF8uQgTc+{y-sg4+Na#ZGW*|@1X>Ode%1`2J{4lHp_ zRnWUma(6eCdZok?s}hJaI-Gbp|dtK>`B>~Tb8Tp$KP?4aE-#Pyx}qB?>*zXWZ-buU$( zPw)JfY^jP{&l*~8i1jb_T_#p|&dvgHV{HMQ@^K=obw~bM+wQ{wlf6=(Wtg8$B3y5( zTeLY(R2)3__P=64E;+;j+$knM^HC}_O)g2kmK09{d~hc%Gx@q5&EpnGwEXEufYPZj zt@!)JF}!9j!kH~g5aGhAz!nKGn&Algq}xS?o{yqp?a>)_`@Z`ydO+=4*pD2C?@whP zhdi{%u$oP4YSp2r4T17PL}h*0h{x7_n4_4$4Exktes5f7a85LRd&b&;spfGtwk!3D z(%2Fc4O`k)pb+S0sRfP6vGejm7TQBT2J{B{f@Eatq2HHN^2(< zR@2d!R0P?1UDs@QmIf)p43B5~iTBl`bN$ioPG`Ecw%k_Knv?7(#7MJ5bnW@NF5z%X zLbREoED<(TnKaiBB2Z z;cz+*k6jeyS*BwAo)h!_`e;f){ApRY3A4bg{3!8zG^Beg-}QDoeCL`nQ-s_|(&ooc zX!NtvpCU8x*26sC2=~;W_opZ|DmvI}6_hfUE_>2o{C=rSq&cTPQyCX!3C*L?`DUBd zih+({stcUexbfbn%TH4uyvGl;rmii*scjny(2j?ojVx@?Ifwo>A3z#4|1C^xKx8b> z2PGk3d5tJ2>T*w3^DIDrSGy{&KxkEL1MCyLhfQfiz3vE)eUhX#In+{Gt;y*a> zo00{qI*PGzKL>iLdwg_|h)zYDHZ)wL1X~X6OhW3^AmW48=bj}qdG3r}vD~M2mA=#2 z1@1GT;8%aSC(6-`alTf6ox2v26sY=wRGbvWp|1vP?;v{nWh0%j`eW1fEO?7y(;}8h z-?J`4+o1NbX*wJkJ4w{bxKiu>V>IjX_|ovnX7%B&oS-EQ-2*H5$%wb_+Xdn9bcu+* zfOFh1;%daYneV45MkMJx@I!$pO7Rd3M))C;VwcVJa&If|4V-&Lc_ILXzz*g1HM ze!a*6mY8?l_2x{c5s&wV&7r-FT9DVgrn2rKadR7~E+5Jgj&&=1x6xG{uf4wKOC2%CTI#6#Q$0+6TD<*YYc*_%d^j05sKr%ZzDJ z96z}^|8o4fG2qS6gWJNBZ@=_~QFLYpX6;sh59MhgVl{@>CHns5!mjqa$EnlwleQ=;lj*O2!Bobb5yp_6-C{a$`tBg8_L?r! zsDVJ8^0>safiQ6@?#!|JYt}OOYdKXMPN4SDE!S^1?Ojs%C>R+dUf*^~;wXJIBaU=g z^!dQd+LYT4yvK}RNbZIH2YE@cHpS;zLuuBK1_0YA`n{bmd}aG~5Ma?E${KyjRx zF?(cug@l)b&-+2;T-u^^R+Kt8Mv}m^z*`2Hn~L=%SrM;vQxYRl7x{mVABDUO4jreY zUgqOCiPenxLX$6%jXYNx8gv8SgoHN|%`Qzr4ObisH`WZ!==q3y3NYX&vm zgwI|7BCOteyx{7%oIh7iNoVwQ!pw$-OBZp+3}8k1zAoX%LY2tVTE^a)-!fvnBHw;u z-%+Q}(d~}IpE7kL569fqW|`aru5YD?M@y;)=*&F#%ulb`REj>a%7awhAqo>$i)Q%< zsZlS2nw8(I6HENrA34v-JJ~paUD}8IDfjn{vg+32?)k;1#H3oM zMCx4k?|!FZ_s_p_YJxNTKP~`fuZuzxlm<|OVVjLe!~j#W$837@jRIb6GkmnyVFlgSX9r+^3n}P7XHI8t z46+P7*ELEvLNN}XAx%oHuj%3WVqPzVGDnfi@YpMoK!Ao+$gK_J-V~m1c+fJFFRKWvBVtOgF zFl8JmFQ4rB#2HCb3bgt|XJyg4lm2_DFBi$gdQ9jaIJY6b*a*J zg+l(8ZK+vqFY9`KJb0y@^pE@JU=ZIz)hCgpOqTWT*&Cj_;|6;FxptmN?O_6#JVn>F z1_5f9K6}-Bdl6BWqxD5TSKJ51i<8nUvoDvLkqB~AAfzESl^(@1JIq-PmHt;vdq{jLB#{T+HyV2iJcoOr z=d3V7ONIv*XYISc%1sxHjzIzgu-y|zBOeWtoxN;!_>g9ocATW-2a9K zQ0=jTi{gU})bfdZcdg2r*lhZK*7tRL)UUfgH$a};Lj)$R$gF-TWF3WZ$+$a8$+Qv; z7mjPZx2`+_R&D@QhQ+SRZh$SZBA|G&e-}5tf_z3nA8v4f>qb}iiFdA!mJ!R2e@?22 z9QLx56X4B<)Hg5K9NN43K!z`)kJ2g1M7t^cg(LM>a_fGgBr}xA5o~^(*>lx*#&;Y} z3*Su&7TGt&(Q$EdIYF-mjb%~=jj^?WL0HGk_fi08_}Rq;gto$j>RyOFSKt$il0G(G zs0kgFST>`n>-Q;H!K=lhzS|AGWA8`DJwrqrebt8-h9|}IdWM;EO0V1QRSlJHn(oy(H%pT z=ARs;PqT!a@5YW^i|ki@fr|TQvPyGi|B1NLbdQ<=^;(HG4qo zw>&gA-=~gg2b4IMpP$M1B}##Eqh(q1Q_f)W zKZzS;u@8ZH8i88GV5sIvN9T<}z+zy&%{JS>qz_LG+aW|`#kg6=C#{kFT4h!rT?`At)yci2{ zPa*xIo&5=c6Bo5mrL{^YQX8nrd%@Qp4<&4B`26hJxdZ0`E!278No}G{%0?!$d~2^~ zzDs7e@d{LNRAlL_OpRxgP}kMw;CNBpScIypaNy9itY(F;g|0ydgC` zrr{&jUdslL?mv8F7%Z?CKMl3JAJyJX&`f+0LzSMZdw3w|Wz~4NTR957m_O4idG{{8 z=#JXKcdVX;kp(ZXYH()WCZRD?B#np7@HFRA+qZ2-Bicuy@Yk=*UDQbHqo%;w@K|1@ zZ+SYCx)xTd!f)?eMpX9uX8d5Sb}S%EO5bQ?>1luZdJa*V`u@#KW_3jx()N zX4yXt{>1X@{{+!A_SipC`F`>n(Ji!j~nWN4eRPwZsB%oxZ&bzCm^9(bDqpg2eOpb z^YsjU(j!N|HJ{b&G7@b=eX?mbEs`+NZN4!40oSRv9ctHW+9c;jw?Pl$5dB`X3aX)^1vERabQRm57&omdz8cR0A0+r!@tg$%Fra(R(#+#7uA5 zLG<$;Jc}1zHZV6K&kS_GjGI-osDdG5CW+8f&e`{`Z5F>rOnh#34H?h!*zF9!yb09t zx=e}*lpOV*_WP?Pvncs&(FVV2>wRNbw*)zQkn6|o!4el!f#(FhWp{eI;w+c4Dr2O@GD))G-(JFzP?zvVhL<@{hdtS1A7x08t^^v(CWs8*qg41 z-8!32dPoRtNg^(Z%F==hpJ|m>wDK2W1w$W|K_oHz3|N`c=B8^Ur%IbT<3@j(>x~1# z8?_B!ZCx!d%S%>weSq>aOg^f9G*fR;&`~5@VN@3${ef^1-?2Fe{0p+a!T<07=FvO< z|MBQ9g3+GQURWys@jX9tt>#!_FSFMM9=bIVH980l+CkPBnKz?x+%ZYY@&ba%GJKvT z`+Cjhys8%n_rf}v+DTB)4XtI1ljo)EmAve7z(gvP#LUv?hWIaWj;oSRJCc0pE>UIl*B<0ek8pV3w)9!!2gklp$j#8Nfwj`^ykEov~$x zm+Z2jIcd^dj0Zy}>K;Kpryy6dg?wR__Z*}?J`HP^VG5-WX3^KL+WVa&RJ1a}iyJzp z1O#rj3qmI-8twiVKQPa?=-0xP$C3wJ*y`3~cckQHUat;LGdQwAT~yz#e9RLGO-|Z) zySr14JIkL5L!E!yo+;X)_Is}?ui2j>$PIu-j2<7e_EMiFtZdCt))9h9! zNe#IDmfru1ue1JZ@(=&Lh=NL~bPQA!P^6m?DgsK1q%=%AM(60xiKLP`=@{LN9?gK! z-8Dwns2x7vbIwoa@i>3N{kZSd zSS(2r__6x6u$O;s`iv!5zqSk$T%F5|Ls ztBQNpUYL&jntzavyZ@BY?82LtNBT??ldSMwEWb@e>9y^xb@t#+kbP6SE*REi12O}Q z=in-fx)Pa@=HPxo0|+jW*`;qbu?Ai!mz4XoaIlguLGF-1(Wrh?b}Hlj?K()Un)H{c zf8<#^+3^74ZzMmNdR1pF!=3?M?e^*uucA^o-Nqw}jo0aGTt8xv@We-cxqjQa%wbRRp{7SMY$fGk_5=`TEiP5n^NtfFWIcH9|U4;kw4G;-O{k5 z-tec23G({EY6jFf`p*)9R3l`epBdsd1ev^c@;il-H{p^yFpS`#W(o?KV1 zt>C*-e}R*m_nEV64UW}00%gQ4FtL`2`%pDk@HLeL=`|d!qxJThqHDbC=C>^)>-r|l zu5to>KZL-L3%XfQ-s1uC)C}vzyoy=?=Dfd5Sn9;ajU!#pwU)@QGvZwKx?tG#iNJW* zg+=}38X82e?zFAeIoOmhH_@Zput$gK|0!Mc^L%Qy|cD|W-W_-(sd-UkP zvXRmZPSWPbmSp!k)AQ8>?yij_Hf-pneKs+g8-GJgw?p<75>LRouv$(rZ!$G*n}1mh zxY$34x$LqeE?5mCIVK?d0+EEO@^13GR<5LF#a#V1aQwFr4CAroq5zGPOOrF*%H)MG zq3-I-fr!}jDf%FtlLk;+Zb_5LV<7QIy`9H`pVo*o;_C4uDHYG#XxDr1q%Jg`bJ|XG{i))0t*AK!aaQ?pY@vy#A zv$Os3;n!a99$`1nju_vTHRZ!Yv&Iu32sw^h{hi|I z5TunUa6HKWzFn>uCL;XnWj#`Fp>)7E>E`XskFz*adg*c3BW37A4`|&8QL;>c?_{aOXaVoYGnQUY(u9Ar}ThG z8};v|920%jMSLUo1&;EWB9x=uvYil{NC}y@h%2Teb_>hGTwEWgmR7nNc-7ERm_b6@Y)Rld7#or`Ax(W zMhx&-Wt@E~Q+JpbD^$Chu@ow5g9S0|%WJ!Dwp(<;O@1%e_XCb3R9P(RH^st&NM1=@ z$!xYmtTMT1`_@3|X|L7<{x*I;W_b1q`CL(JC#VRO@jhvfp#efH0Wz#_2A=s1FB_#| z+u+KIH+~~O;C@)bs)UUgsR6kN5&~`_L>c)vWC%b3v9nlTKs#Z4{NaO+75#$-?6O0s zxW*K2xl(7zg-E^gQ#hivBulhg>v51Hzdspi z?AyAwTD$U6=PO0_rnq#l_1vcU=Wh#(1 zEzke0e<|1XFZmn#LKDqL+5aCGuFR#3Dp=Am;e5^N$ppG%_#-6Km0IR|EXZybo*@%1 z8oAs~3`fZArB9Y1yxXzbLivoE+5=LrVC>y7c}bXAdZHJE%g%wf9+?GJl<+SF095)D zpWLIXA#DQ})`BF`2bvQp(E5-fXQ7Lv8HByT`Ov(%S;F*6(QVq=pJ$uyc{aZYPtqDM zTL$I=RtiPV-*Ze9pXRd(DQq8S3R!7SEwPte%nxi;3#x9&BL6JUm~F~$FBLEbX9eq- z{FbnjN|c<^FG?_m0rCisy)>bqnZnX%zqR?_=hfR8jFLnjcTa3Nky~0)-J%L7ZmfS$HM$HywkCFO2C7UE7en0)MH&GL-vtE!s zN%FGFl`L@uZot^A2fS>?Mm8XJTZ`3G?0o!DM`Sv6-gNB4b*WEjiyLli8jwn#{`T4e z_S0VU{tqJ`3wH$0ZKLygoM-n*wf{=tAcN?3SPc7LAeF*CHslMgGwn-j)X06-c0~LF z9c`nG4!!he@}wU@6KS`+I2oUL&bz<1cE+MqWj(4>auzE`^_uH3VeC0FH-&|Osd?NZ zqo@nlxB0^9XRA{$)Mr}S*~s{2r#*y&$Hc7PZm~NwG5aeuN2W7LXt!hTclbZlGFjX3 zu;{BEgle9_I_NI`Z0xxzo_*=JPUpG3wBr}tFTU^5LmnrQ(40bo4k&%7>BFfd9V9lg zi4`A-X?PX%s`5S8GZN*#2#05bj=}DLx{PvY7cgMgc^Rupw@j$J}siY2T%N^naLeKeEA`l11ILHZp!Aiv1b5 z#3{?&qWeLaVUueAq`nWmm^3K2AC!Lh zTb!v2H}7lXo&l-BkJ6R~kGX2w{KL5cl4AOpCd6H|VV&gSoe^*IVBPFCu4|U{ktmzc zTb_bxa$!`KMisD;h)sju>(7L=9Q;9tvr3icgMfw`={%kmUL9yAy6xF;o2uA@H?-Cb zr`wDiklSTV9zcGkE0;#|-}D{r>SU^Gf zb<2K0ud@l1@cz{VT(dmJ7AjEA^{8?3a0;0J1816SF2)r{y+5|B2^Q;w<~m1OAdW-p zov%@rgIdfS+)ewZmUD0XPMB}7L|v%^FFQSI*qH@0F3LgAe@Iq;*&@dO%78<*YQ$o9 z37S|w-i{Fs1QRhe3#Zr1>u&(APLdfm!{g+vg?#@hc)5<4`FzjE^4sKnyPO8~c zrY%@{*zh1^_(^;5&iBJE8b#&0&;v^#+0XDY?|}S$Zpo?PjI1>ZbpH*~2Cl^3C9kp9 z;kLW)FztOHEAo)qvuKjoo{fI{_lHKu>N@&4)Oq(@W~;n(m&Oj6Bth^+toh+I1KO3d zmOv(RQkfW3_CoYWl0tqG@f1lZIc>Vb8k2$PK|b+D?7m$*VCu9)Kdr=fOU?#D24>uK zd33Uxtp0u=D?@3R4{6Dc^iJ1XV=YG}>kt$D@tI64e8phv5(o34461Ae?JX5@8xsL0 zX>5*_`_02XoH}XU+}}G2P?hvBuXm`w#-DXg*8vP9+Rzk+1&E&7ypU~DA;PfA1w;NS zgf%uqO~MM>A+qpXW#`{V#hfOO*Wb6wo~8Prq&b@wp=Qm(SGO(Vbhg((a~fIkdTBcYZ9W0l5&Qw0{2wpy!u=wBPptu z;FghWIv$VN+B|-7xQled?O(ZE;*?dTbSbHVh-b>-~ z?oL-z>A#UUZnvp$X9-EN_m26_E}k5Q%O%JXIB>wZEzD~yt}!mES4HnQW=|~IgiDjM z)Pc?^4sL+`Zn&q6IzM-mgn^-3r%&DOy|(5G(Zh*N>GXK^Q)mv!**?MddC2fCBYQpj ztV^Ydn+=O2@&akL<;qiM>c2^dZLEMX%Ez`#9Dg3{2Nb{ZC}>rZ%UrYhGCmQ1Pt?4s z%YGU=Y?0A1|22Hh4C*CP(mX(Sulyhb_hNTse0W-ZEyy}4~e!GVUF1W!$on8m5 z?hcOTU#5fq7>&&@Lq>n|m_9VcdP14Mn#B1&yBdQ{H#X0+Zp<;%t^aFWwmO8pyX2 z>mMX>=sdeg>FeBog0NW9wzEPVV0ct$Ropgi_CnC@Tj#|ab6}w#ws7g8R)UphRXynh zOqRCDCEJXp>pxGA`9O;@FCxhdYlXE^WIWTGK9~i1wm5J-kL6kT_&UhWYr32emTVmt zao?kZKeTNoXHx4ygQ?zyg1PSmY?7<_NX~sLghyqA+u3mV&cU-or>wf6_F z$fQmGGGd2g_(UeVyuNkuWq3K5fN!ff2|IfD0E7MA2>D5Xf@O109Az8o=$|ED=l;L9ZI>rT@{9CNkb}%wF}Gi93hU=*hA^XTwV0 zV0itTp(q}e$LSXJ>aEjQwUP=(L-}GV2c_spqc#4lLY+zV-$rz;fLpoHg1YEbW(-a0 zMx%eNt$Szq%_h?K>ad~VEI&62-Yj)}wQvo*O?Ux@G3vE~V#F@Lk~wdDA`!L|=w)^) z68$B_U~_K6{ez$GI)svS{`E8G!kd>|F)r)a3i1tH&K>?U(Q!W8>9v-KweD7d!6IgP zjF5&8mr;+c<}&}^v2lZoCoIX0;daI;&gOE~2B z>QZCpnzQMH-o)jXUh%G3i{3Z4jt+>`S6)St5+boYHKUXT>c3XR$L~*_oOljjae|RU zid0v$7rpDak!?(3wgnvG>dsu3OWw9rtOr)|JHD3wP_~=g?|qW_&K{r#^R@6RM$6fiIOg_xD%GsE9E+xRPisw8S{ZI^HQ#VMj{qR87^gNp+T6IBZO zy@O$<)`#5^+k-S+*MH7Q8dl!!3q6SCtKlnMUiql0{J_p*3{i>}w^6TB^QyKo0edVH zd83Jq+_>pFd=5Apj=Hb=5xGXdG{o5+zU!X$QLKAM)Bwr)d0dVElM< ze$JI!Z_MW$c>CF%E?hmdHnu3W{K`KY`L&B{^y|{|d(4uI{mO!JU&^_pChojBcMr7M zS83OV&N?VDy?)CXy>s1CwwB|_mZ(8I$wYh6mYptf7INR8ZGtd|=~$c=0*|*ee*ZPw zl%ph}Iyud2{`_M1s*jo~&yGUm-gADde{iKJ+EVlP7CD!g{aA`1Yze-Sg z(fJ|NK1t-J6}>A8VzzpN;<4BhDuMHy*xI8SOFaGl4Z<7G{C8CB+>IpD5tm)k$6K*S zqN;BfMfzNNw`aK}2Rm=R2Q%>F{MpGtRQQ{J!R2FN403++W9gEg?+~2Z>72guTeSKG z&pbck^O3pwN!i>XJN|=DZY@H}lT}3D1h(Mca4_Dm{mVdH-}~C*Vn+Jvd9H-` z6!sOWT7B2+XlEkkVi*ny0hf6zAwSC+M!K?MBAWSk&t_1_EE~b``cx(p?ESij&taUds?yHiggV52@(PozEElv7Aw+>O9%^f z{rhE1SCH&yMtx^SsE|*m@lhUHtBk`Miui6S%1}nOP9t*=5v1l8z@`3L(yo)3u4wUT zZ`WtNw^RA;TBXP?Hc9e7ZAocYg2F&tr|(JXZJFc}dK~7y+gSYj8>dJnd})sPcX?q9 zsDX4$jcz)8qE+sM(6vf&#zDwe<1U>NEG9pNy1l!^##IPN=%xOE>8S##QXG9$qwsRA z%mc0{zM?W-P*X>=6q!JW=h6HGrM1PTx{;4iV(MRqnYal_aVhD*_>K2#;;$T;f3489Xv zhh>01*Bvup-p3lQh;3SkUa;AF4Bs5zo%++KUv7J8$>=FH_pU0oEQ}#`VIf_<1op*l zuc>l(!QayV?l&2MP(fmjBdcFx&XU!#7Qr+sWY4DJAZ> z6KaG^?;)`jtJ@{lpNIuR)^JIt+9?+EwSnr)@Urc8f~y~~?rE%}B4S?ZjU0MV8^_Hk zx?4LeNKe|mtA}K8g0W{ZGNhi*W9Ghty-e4LMMytfb$zZd)s~WV;J(nd`(0G7m+V1N zQRAf%ggKwq&YVd!0lPBI`q3)Vl^C|8AN}sfq$38xU4OV0+1#+PdQNf#RkJg0>9rgm zsMu~`Qzdo`=lk}kB&WagdI}idC(b;mZxGp z7ktn|IQ zO7m$FQNf9YGLa8&uw6j}UyY4Z3pCz%7Dh4fl8>m+Z)u7pg)>EAzGosiex=9#rbh zoY3xGe7;Y|s9zQaP)5F3XQwO0!D24dL=IbJ9dn7)V~kbj@` z2iU=&%lBy);znq^H%C-FJ0$xJ9g)A6{Z5d(KDgu6-b+`q4cEp!2|qbc+`S-sl#Ty= z-{Y<;7SM1_Qlc-m=WE=nWfP~Q6jbnw@IxA9B_6%%_gywKxdnQBOX>&YhIoeHt{M1W zVh>%Zc_|A?2h8}uR;KS|jbq><=`g3cpg%3~Pm|0klRq)3v;6^?o`S&EKxd(9KS*>@FCku>ig{5SWm5GcCrDFszSRIVBJ(j?PafMg1fgQB~BK% z$2gbWw;Xm?zfXIwoLI}i-B-;|4rTr9=;f{dl93@@Zte9~xT^>KDxW`1SSblFX0Zc3 z1*Pzczb*`nBzR^3n9<>+8}sO-KX>9_`P0Ez^BYwb(GHD|u=r%|C^7NwD7&UvFukGN zd&TbnxwdsNVXuv~hgxe3jHZlJXnnJYo|(FMhSg$!ywUVGsfc44(4)ny*MDBNqOgF~ zc%t}KYN?B^)RpCqWhyoyZ-qwBLtQ?c%q35L9z@>i(W2%0&1^0aju6Ef+@4eWx-Sx5 zmxo)&<5-co`^9d2O-(zQ-}JZ|q#x<|Z1Dw@T=)m%I~yfuEIWE*;ZDsC?1cUNtSM`da$nipI!M=Y=bL?%F|oYM}{? zFhs&8z<&ErcTaHR{=d7-7kStY8?Uoygn;()UsT0Jt7dC+SavEVc81Z(j!%vF@{X|N zbLtw9m6n27H+b1qcGsWl;tx#?vj?x`MOK~CZr@O115TS|lc>>D%jhG;wm79lY2kc{ zABM9gmvh2>Bd&o%b*8lk7ojTsg(VwqE@+qMYwIJ_%bIgx+`^~ENVr7;_Z37nJmDR{ z==4_9Q8rgV2E%_%D)wHOSBat&?Al(rn`kU_cg4zcOF6sOmo@|OPQ(M_Zr45NEzX5o zCT2G8Hu4K?YA9dX$H)P^n_8TnELUyJG~oU#&@dAe@r`z_{(A=cO;2ZDxhW&+YIZQo z8U(MH;N1)tqr44CrjQvFX?6sOH1eo%3;q z=C;|@x;=eWTS(M|=)M~pN_96~wOM`wY`~xh^l{qw3QntTNGUaeeD9i*?xeZDaLkP2 zzO*+@2C%NoGE*3VcxIz5b|O_JZ3^`}3RH$*3$+3Uwe{W@{8)R_*4ze;Xp}d!y)&y> zE(ma8q=RHi!9kr4V3MB9a$D%DBu5UYXF&`ZzORzf@muO)Jj?1n2y5hnpW3>plIahd zn?6-N&GCk(Z%5qswI-;K&UKqPU?2v+5yru~MOC|3jC^P05G&`(!ZZGi*SICZYh$jK zLg6Ecz%X^RKXk6XHP)Bit+G#DeA?Dsa7E4#(Yuxs8}Bh(#-^AH3!^D&kNXUf z0LnYD%Uk!aFp^go>NNQL9L*sj7ImVlI)(__vU4Xz{YZLbGVey)eD3oONaRNVJdolY|eZ2bp8arwJI}@e{Tf(Q7b%d{HuVg zaw-;1VLyXk#3?5VrYn$>ql5Y!|ENb|+_QI0jz0d_~kfxdD*ANn5tA0ZGYt^y@x+s!iN zVQ<*(P1xW^pD7Wj!>moobK$V-ZyGzVVYI!Ii33(|BwVXIGbV2t)P?g$o$0gOK2x+T zV{;7ijJZ;UQp2AjEm+Jfl54R!mTZiYX!?eoJ8iWO?X?<(vN?d+)cSX+TspZWD?=z> zmuak7{pGu-m^%HnvG|(KXz5?9Mc0ogAAAsefDHI@?q<$HP~&ZqOwwPQbN=W89dW0Q zpQNLAFeG=QFGD76l|{~b4YLv(JE}e=R=8*sCLL0%Qe$Pk4V~9ay`u@;jx}`vOz{p= ze@K*{_4cyNJU+VEL#Rxo&q~#XfxnlUYveYaQvtU}-^$@=8AS1R#?4J)U%tZljL^ zk*EZe&&1hDke4uwXW0779S$%GhilyWduvqDL3mq-5nG?{wskZ+j0!DuP|Q)WoVvPJ zpRY+5{;HqW6u=m#wFyhrmZPRAAvnPWf>hD1I6U+dXE?E`e4^O-3)=bFdI}N@VUuouips35&29y+7znQbtTl&>5 z#3A=4f0FmjhJs{Uos?<4Pq?hlw6@sKsDz{b@-6S?v(>>^e71;mhXHNEgj-rnJpny{ z^oLbflOUEXDpg`>iyjd7FfzB{-r3z1C!5usIB^xgJpYNbg~c*)6S$6 ziEVx6-sJ2)CH4yK4v7r--qoMdOYW1*X<>q+x;M>4f!DiaRE3?+%_0t_>)D--9qlx2 zlu}2LK0`XWtr=FR$8=PBar?h5ehHR-OVZSVPtLuDe|!{xBg}lTESB5w!L2uldrSPy z?1M=Af6fhuNpCz6*E8^Fa$u)4o_n5ZjoAFW)gQ`dab&{KGc+uhZW|{ud}G4YnE=MI z5)rpbZ{Bqw$B}Yp&6Tv8vLng=u?6INOw*hiOH5W0#u1pN&`cD}a8s`{LyK0u_-6jONIz=U@o;;odTd#qs! zsCbw|`UiFiI}Fxq`F#xb>LGznyeG3M)KDT&9Xd5~{4>>FbB}H<*P{^9m6cLO*Z4V- z;!&kO*^$*r;fcZZ`mUstbj59?%K1lf@}ij@L_X!8WBOx;4&T514op1A9^y~O$M=x` z5GSv0!^|I2T)Vn@J9!%8QkU#^dB{+jIy1l{k;l$-4xhcLBow?4ns9sTbR9u#$@m+@=rJuQ^>;%=!^@g=3M$Jd zck69ctptE8-9L0lBU&C-S5k&{SU)`if$;gC_dSX5n6tjU!x^)NIs*u{YU>I+gO>Nh zNuF87D2q?B&Ro7Mzb%-5pM;TE9=T`c^Zdt7J8qJrDEM}nPC@ng2d8nmgtiingUOTQ zjN0{&a`Kj8DeB{YmH>RD2@G%c6q<^J|LBl;q?2UBmfPkU(6?~eUYyM~J5!0@$*If! zqQ}L7-<7wpsCBj6)zxe)O{IAMMkB-S&0mCPgAri#;A!iUm{Bxzr$ji(k!nyb#Ofy7 z=Cr_~NO7fps4wm!^r;O_jzuy?GPhQ)|E;1mshUYZM0<5HsI5hp^28A4qr=r#6 zYjXE)+WkH_TFO*uV?Th<1(mr~$$(ZP_fgZdbzfK0J4{WNMonrsrIE~^8c;DPp}KW3 zQH<26t0rV!tu;a(@|^x&>V7B1_&j-ADHIEePLelK&@$;@SIji3f$pgqc{~E|?`*nl z+`@?iJB(41vlb& z+-3Iy(0GF<@u8Fj^o5WDo5%hjsH$Q`EQIV&|y`f`G2!7Y@SXMy&CPLqSHT7D2jLgMFZ^Go&V5C+BogzV{q0yCJP7rdd%?%K z-r-{Ocvu>O5;CnDl@s69Fy*@S$#wD~EDN~*%yP1)S7t*vlegt&sgZx}-;&h_1xak0 z_HER=gFb5|<%f)GF5N>m--xK9=b&h^11*P;7~gO5Q5*H-ds32fS0fFU`}Xz|%^nhf z**d}NO`!C!j)TM`Efeu6=WEy07_4vtVO_^g;uU51WAK9-z_ef42&D7KmfL z3;y&NEq$#rHc-{i^6{}OqXfawx^P}Lg<$Qar(o@eV%el^}M|HHJI?J(dWIUV+pY zoab09h=!KLlfED$Ze(ml zHoW<*;qQzpHP@lO)NK=27n&P&TPi%R23-zDnzov+c6cO0^VC6+tglfdu6|=IzMrQk zk9QCZ)9Zycl_l((d|WKDeA@NVbXTub`noJQ1gJ6t{f!Xg?{v|te_x1weswnOJ6wNi zKc9JOG&e_)Iacah@=%)b*yA$ov;}`Cjfd)fcOhV=VKju+b${e}O*@{~SEBm(sdDag zheC*K{yi?UfFC1+ES=Qs=^vuqU@jf4Rz%*ieAcB6OoWN4;pK{bgoNbpQI!>gEq(Ei zI%7d0FL&IVuTvA;yc({kC4Rlfe&`&l7A%j^t5RNhS)_07+eR)*1j|d=3Q&0OC>WP% z7gCs!9`b-&3Y0d9{0Ebi0_47VRdCE-)TOCGm)gWPsvjvsU8rjwo6 z7wQeQB>jf7%uSK|%F4dh;Ks4U`iD`xo{$imwdG1 zx{R0w5M35U?{G-Z6tQBS7_zDBhbE8b3rvOD^SUru^J>J7P#Q@Eysm zL@WKy+nV3@?f%^Gqi`0CDH`8GDF}rE)Pc4q8*I01P2~}V{q#Qyu5_?Oza6JzO3(o+ zQeeG^sd#WwxI@h@I{iF^5d*+bp}efY*lZ(gq}b>m-)B;E`^`9FilLIZdBf}dd-n&s zEU*(#iTWo`H5bf^xTPZ(rs!_+<2+dR%C@VGKgbv1_)y*8*f^bZss24AC(q95BCOu; z0Nxy%-{(5Kpt~pnz$u4JiLjWU@$PJ)(k;PmZ%?X7SU)Zi>G8@g-BW^YOvKT#PHlKi zJODJ4cQTas`Tb$ElX-ht#(be|P~h|f#`7o(<7o4H&$=j54U3!h^S0RN^23YxM!D-X z9w8rt`$>U&U9NQw&&J(ZqKx8>R zor>!U;Xb8iiP3xd>y7)|z^i)F0H>|rfJ0loUV_1QiUcYH@N+goMMRI8QK)}^wK>LB zkF~DfZ{ORad^8pe_oapgcTSv~2L!kR4Tln#kG!~I5v5~c!J*pn6Q<9D z*;Km*ccRr!b1X(Thqnd>35IBZ!K9BskG*r|g!VTd8fYNaLv+Srw>y!LqvbO^&W778 z3>wCBak0nFZ0v(ZlhLU1w~6Osu6pAsN_)pdo*xY?ayu8hSYXVh>j$Zth%@Fe6!&7B zjuju3&Nr6cX5H~=c@mMA9QC|Toj2x)F3~G$om|wiG+0=#^~64E<*iz{#DZCS?7{AS zN8PwUReE38`$=E&G$fHmP+m{#!=I8D zLrTmu*r*#V>3t#L4T3YTCgn)iAcdyjH}oI9mW*lT5-!Szq4iZ#q+%Ux@N}<)h3}w1 zEEV1Q^O8fJd?zR!k-750bG;KlBcA86LrqPp5oB0Zy5(<(xrvcP&m9@t^m_BLdC2%= z-Q)5FKrMRnQ&~;!{X6VZ=jDCUQf7=k2_TbP)hJiFv~N34k{LcxdRNN3hhE?QCc=*F z!MWPa*)~A<-ZY;(Bg-67@6q>AgYyxohLX3*P*K#EJ)ly`#Tc+Q&1oUU zsjava=@a4Jzq)yRe!UR3?{TSldPlXnYi6U8_$K!@gF;Dlp%84bWU982Y4ol_+4EKL z>D2saHYHnQ-W(NaSZg19^WDX)s;rq15W#H0W4%kh3*%K7DUxHI4#va~D|V|tgAzji z>bw#xxd`xT1!3ptPA*jlcrM`Y}Bh@bKdE1m|K7_YM6W#mp$$wqrRM%$DXRz+9 z=}aKREN&0)hi`V@t2mo(pW0B_o4?$Ruep6+1 zNU*cj$?40%crz;xrxyd*IRbFknafxqN)aQxd{=O!pEhR7RQdIhm`8)|DwpXOT2q$; zG1zMX#S+R}-t!lHty23^C$FVAgkMycMbbIX3HWAx)3aoj_xE7K4Tp%EUemdmpgc-6 zB?+~7a#{Z@h*kf@JMrq;dT?|N3_o6tAWJ)SZ0 zbm4FCLnPz>gwXAqjL4Gdt(!Kx68T-l1=B;odD#VtzR2{}gcF;v%AK~@$!PZ*T5IQY za_4Q59L%^Z-PtjY5c{qO}Q2mYcjdEK^2!PT+yiP>8(5*>g>Ke&sZ-JO9`Dg zyr8&*-Z)qcw#Fbj-^r&t&0J>87)n<{hlmfR!Typok5U|t)NaFUR8;^}7>jOi|TUjOi>~hiT<+)RmY(U|2O7At)G9h@DUE@nP zp`i_LSgd6_63@?{QbZ38Sr;J>BIaVxvazX;#+}m{TWU9%NJ8vX-jnIYKBOyz^gn~v zEOJzeL>bXyz z;#I#8_<9-rzNozm_FBPI9WBnkVu|l5T=E8-z@su~?mk@uB@$ANI>}CEX%>q2)Hu#zJW)I#&Hu)2XkMw>5se z(T>GwEN-Emagbk@<*iXAn)|26SyAe(h!5jlZIV`N$dhjgdit6yDS&Hq@G`T#l%$JGHIITXS4S^j$8)5ME#0rj8uuuq75F!J&?o6di% zPW}2sDWV3^LJj465Z(cPN&`RlwQ-Oz6XVFcV}48pK0Om{+dOLduA+$&D)nI zx^>&=u0%Deo$bIkw<-c6yv|yXF`p|i%+5Cw^p`pp=zczku#??d?fO5-YrgaDLo;yW zy~1iA^R)h@hU=RlQam9YY22GtF`Ys~jX!IRa#GF<_#L#{acPe%Z~`G)zJODP%4m~vPhh>xy&@AiiR_mSTQFPNIKG!*e5 zHEn11PdsV2UBD{29-yZ2cEVx@XS}mDm&OlxUv6~N=_*$LVnGvlMOVxuM4g@Z-1v>O z7{wO!pLWyEx!=C1xNGkp&04zX-cA`|)c*Ta%OG=uS2mK->id;zZoyHd7cx{-VU1Xg z7Vk3arpPTIz;GX$4)vHe_tqJ9H~wV6(;Qnt7Y(SUfAy`nX7;yNaWJ!tXFW@MsOpz) z8=qAiCy%tRQB7=tU`_-jcf~dPDQFsno1s|ddLZZc@n71)_I)m}XelV?dR@SEeB9*T z8lSaN63$V^Za#e9i9uEf7DIw#QmFzuRHX;a4r&um343;pbrtu+UQT6*WyQJ9yB(#) zPwRi=zF&haSiu*&mp!$ub8_7Uvb-Ch`198&n{7ILn2ii!Ynae8XhQ?$;s8JO7qI1= zTV-n>P~3{hw-7Cwz8$NK3Adh|PR+SVL2^rWRe|uaiQ8hI(Qyvf7!A6|Fh;Ql6lN`)v=V`dpM=!c50z|`ib;>cjtleWzu2+RDgH4iZA z`t#bfaW7IKx;(XfPA9qAO*?$5)Qnu{lqHVxQq;!;Z;RjbTu_W4SP zG93zs0q&ns>u-Kc)1&f?d@1ZBaiE%ipcTHz=!N4YPS8VREseJ53zIVnLNk(jV@5T~ z6G;Mq$4ejH&t@Ra;YuBfN$FL`b>uXoPM@qUtGTugr93bJF&YYx%hs0An4-YQu4xC; znH#6XQE~voZ^5KLaU@5e6G6i&HJ_zn;@5c@qJ=I(!jMg?N@luCBEpsZe7z%{-rS)^ zW$C3pOG-UVAjQmYw~o9OG3ioIMWe=(7CD~mlL_o6$_DcxEWfZCp;{~#Mtk_}M6J3J zpiY2B$c^#q^pN~eIIV`0B+X|fNhzedoYMKDg=2c0>n6P_4Rb@=8}5@t)J_zCW;U^^ z?vYv3(Rm5WOjQogyw~FuR1J%5#(cu1-dTehG|xaDAo$eNcB-IDwQ=Ixjjr!@{jxVl zHqxFFcaJ(lYF2e<8oFkOVZjZ>RbQu+6oKQsC+{Kjn6_6dBFjuV2N52a_tAKj#%trw zSfP1?=sWct;CuNto5>{ve}{i~r>4B!U|6&p?_5>$s^s)JW${1;%$rh6F9B`WSSG_k z9#ua;<=)hCWAsP%O8mN84?$sL2S%Cp&g z!3L9BHSg2h{&CbMYo&F^h(T(N)$rgfcT&r#JVIMZ;lng}etLI=|kPd9at!o@GBr(}yqm=FGP3_@gfnpx@z# z{j?o=GY{rswq5&^^R^hfwy#Pr>4TqKh!yj|MJv+Wrcryl0ls;La@b5hw{fefdgFmG z(nGE{Bsdl_o+Z_98VH_Lyv72xvNJWwpfMU_ORV%6CdUiC{F%J1 zm(NOXwusGGlp`;4TuLWdi79{7;cw@Tm3ZsH@&DF~82`~7=cU0FIR4wN)5LaoGZ`eh zQ728UsCg=_&27crCV*=AzCaYmjV$-$Ms8z;BCpwT##eG-sy2LNeAtLmUg5$nFpQJ= z?l|iMbdD>1VvZPh`h-q$_QiAIF>`Xu*0lMu{<-g9542Z+c9^QK7OK- zTc3N&xgNlD%@ulMRFVH}C^Xx%lu?`L;Ob$1sKsTg8@7_Zzn8 zV2!aHX-i+kw(w{d|DJcq!oR-pe>jb9-IP{db`U=LW173>vNXDLGsdO#%LgAF@S134 zFg^Em#2SU3c90omUD4#O0C)N9Au+Fm?)sUI7~fq82dV5FS@A+jOhgl9Xvr$y^u^e= zKZ6#zSUfIWPR@2$%CYI!aE`VhTDB#XmHT4M!Sy@(;;jeKvDs7hq_NerxEYJHc8p`Z z1%YGAHg?LoaQm7p`cHGrjnRANOq$ z%N+o5;sG&Zp@tzLUUl=rx&6DuxF!y{Xi zXROBDPRQGAJOQTXc0fL86IYH6sF#i>PUBdj<4DLryS7pP2EKcn$N%y<6(iv7I5szY zU|nUq@zx~7mJM`f+bgeT9P5|MT6qqw+mWWuIsV^zt3mAZ#nQtEeLjFp|E_oZD)u0F zY7cxpO9r{5t|y)%Nhx!^QEE+3Ks;SYw0SqZ8M{<|T~DW+~bn{{DdVFFiUOokjF488KHK@FW@sSbP%&3?c%_0|e~ z(AswD+Ux6PU+`OR*{AW#V~CEC*Nh%SErYTs(RWcUd>K@l+r_H z>!BO6Ehk26Uldc>&`apDiYF%K+J=xuUJeSM!lF@fGe^q9u^V20KHc=yboB|>H2S3e zdJohs_8INmPv<2g@7SnijG5#xs&V2gfqc3TLwX$g1djR&9+6B|=_JU0XcOLAe%vM; z(KmxAjslsT}<@G z6Cb#cQxgU4;@L*6 z>$;b(7;rq|s{v(fShwEzwyv~0?TRk^&5x;bsd*dA@G&p7ueP-E@62OfAKw4Sb^U4k z;IF@?8{hUKY?}VfM~v}u@}Sy>is$|ja!Q0F^;t6T_+y>2JoIf^`{gzWe=st?vS%Lj zMjEs0*_J(RLf59nmtn%zXpYs+t7;3wT==IrmZ$ZN@rt~z6XRWe>!$u1d}KRu247VSMZt*p|UtRo-S<$kbD+iVZ6$|G)MQUBT- z&j7B!rty83+cDpdYxb>1KhE)+zLfhne)*Pz!rNOA;;jfQk2Z%)QC}|`AK>6ktkOn} zG7dcMaj`7`aK=|M>7pI9#*w#n#E=j8jW+M4v7uZXo^$BH5nB9_&we6Q7rOKMS0YG% zP1|3Fhb6fZ=P^M3=$pwf-jLUuK*))y-;_|l*?8MjLhgN@IQSYk?yqUpd8gux*57VGs+r{pK4{2Px~87{N3W@l%NRodP@sx} z*P8=emfs=}xC&eAY8&|rT*Rm;b)gU1*0PQktgbuE%O^ z+fW?)yS~`~m@LP7pLw@!96w?gQtvim*Acsnb~~`o3565IaT-2bdGXlxtn!vm*o41U zd*;wtq~=Rsa#dq{;x?tTZJlweA$=B;fkY?h&Wlj$8z!`_ z7Z>Wh;hNOIr4?nv!=6BfOBE%sUOqFOIM?)ah$ z&PivU-aoHgWOq0X?LUx4PPM$P=21D*Tdj;Q%Tfg%h;7VGW*^#E&*m(zF#sA5;Q22c zJUd>_ge(4-7e(gVuqL1jDWt1;H*gzi48u6=NGo2>Td~}yd4!GxV((rl&cvByJOY?E|`Q zK4mEXu1L{$2vxxs{R{mI+<5GRo?5AHa4ukCd$Z)%94#T%Z9d&0r3VoARX&C z>TLa zSzhTI=j8IThmYI zJxAuQI4*}j^WVbdIF+0+Fq{8n>Fmokq*Kpb18@B`Z8C)si;IcjbQqquC*XdAwZ%)Oee`z7fjS2*cv zea+MVQQECM>zp%B%)B^{5p|d+=IJk9l}^O(sQ246a^N(a|DS`Kh}e^1=NI1?>CMMC zM2K}>W5~;u+`c#$K58AZa_Q2r)YB%eR`LcXvDpseBfaF{U9TIZwDa*RZM&LS+O(;0 z@XsrWE)%Z7n?B1uhWe#gW?S0kfBJ$AY3lUV{WkpR^xZ4d`3I1<@33{QRCeAF`*}}W z?+4@K@_Pde}H9ep?c z+i~na;p}OA`wQ!F;|&Km7h;>c19Izo*8(XM-`w#nr{0AOJSpwcL)iU&| zVN)+l*;sw!4FeI!Ji85X@?|tInT8)`Af9&dM^B~QOD{y1JR`=X2Y07)&)(j5<3GIa z#5BD5G{1qz-7fZ({O3&tV<aN58aR zRX;;+lybwwxMKy-ryw|Rx~zJO0QMAEWn8xmJCGJO>soTzwPYQ(&qg0PJR6%{*zl$P zSf5s$cUGEuVgFtKoj&*UwBk~HX(m>H=(F1Fn~zx+z2100yzdr{iNd3BH6`C`qHj(e zR43&}iA@aV4qJIFa$*eWii#Y~mU_&iryNWpd(N&5`-7d_wIxmM--%~xaOFlb&TO$y zw6`FD$38UPh#PI_W|HmkA>dC+XD9isWK(#ei2N1zFzZ`VX1>FLAuga{^Z2t68`m0nWd zCMPG->t54;Pd&31oW`!eZ~u&sYdrJ#c|oi`0IB0vk5V%Vn^Q;xca_OPW5Q^4;6hQ^ zhA60#@rDQ%q%LyHh+|UeHKx6Afe*cndBYhRb)1N|d}1O^zx-8gXy3HA|LRF;aC*25 zG5Z;v1Fcx_zAaR}%QDAm)uG-EeO1ca80GbPRrK&g8|gg&d5S9Pns9G=Gs`k!81N#md1xBO1;nv^^KH_&#F&pg0qIy zScE=|E7RXsm6BCp8(6d3_h#=y*z$71;w{=o-X$f#!Qs?Ci3!2$n);HNYF7$F0 zGB|1aF(1T?4^O5mPky7|{kS;wmJ8GH92a>qP6r>p!iG6;u5HPOj5y9N3!fQZ=_8-NeL9_S z&1@PT8^|?L-=l7#Jk|G_WLjiVoB1)vf-f<;`iybOITU=vZrr%+i7UCpP0z3=?HXIY zid`LNaAY`bI_p56j($q7d+(KLavJAs%{elt=$svE;kXr_K}n#Pm?Q z_AOibcKoNVxgjkdoxqhEbO9GtM0w2?7ar|qEacRwO?kAzYfa#G!Fiz74ms*9oWDuH zZ77R}5yD>UOeK%?B;@UOkz4H2wwP*TjMB2|Ee2{+#__jV`bOLIVZRLaS`TDvyxPch zU~(j_ef0_GOMgx`UAiqzj1J~;Z}fs=t-)_d=V{2GX#x?~8SYu7$ZdBcUFb~~d*KKa zd-~GGUYhQFlHT2iEW+h>W-7xhtC^;rzM}-qI6P7uNmCOe=}otuS3Bq{{(@J{rQvZL z|M755=Ug7f!HH9)_lJ0N5g!+K*<%z(oDDYN8hFZdgF+}ErFR+aeYPSu^|Et(41)a9 zm+UOAtqhcfmrgzNXzPV`|E33q`MUpxzFVHaO->D`H{Q~J4<4hVn#T5Bot6*d_`jU{ zLL3K4^$lQqyFv4f2iR*1W5>}rL>NWwz+@TkIh&{pHJtQ*%%NR3JY^<#XWkBZcM1^W zD+k(z52Nk2#b!>1?UhT$RXGoiPozCJ-UP?}IqkY>T^gFe@liFx284%;wDEF7^STv^ zai`qJw)utt><@ig3OTn+?aMR8jZ5+|!?E#Ax$FYdgBA>u>58Gxrr#3l*~pn=7rf^_ zI60Qq-Ppg^f7ie9wP|1qKfk0w_JL*J)Fx$pt(F{k$#cEAAAl+M2_3Vz>cR_*9xabZdWZ z{K$IM#b>6$QM>+&qq@$%dG2p~&^S{!J$1GozyZdR&-2Pq;|d<7G)GOHaFs6OD*Nn< zxO%QI;XBjtSz|&LJ|edo6S5lX`Z|xCci<`?1H)r!^VXLl+5Vg+=3bcw1}74a^Yt4I z#7k!91r42J<2c~EihNwKHVz(Tchcopf_ilvUwX*ee(E;)E^X|h%zrn1=tyhZT7HcD z^3lmOd-WUOXlZ)BDlqW=-}q<0`FsUiit|rbfl1svU9)<1`m+!Il?CB}793|TjFJ&2 zVdf|%4oe>VZ3^lp!W6x{2ZS{A#gjR$Fp-hLWl*PMPMCudU@VVKjf7lKNI*+Zkrb7l z5@Fq>){b-up$X1Ukq4F2|D}!qr=^MeeweTmY$m z;=t)xd65bsFy`nI&F1V9lK)M zdV-pMY*m9S2GA+<;`-G+KOZM}(@C*nH+{Ktvkm{`oI;M%jogUO;exaSL>tsBf4PO zl04^AbdC5FSN*g78?9UTDjv0*Y;V|Fzks{`g?Fayt4}geA1;T+hVgs#6X{!@_?m`O zH|OmNud2ckwAqK7#|7wFF`G@#t`}VBqxz*S8~Vq8@l*8jL;o#(!@rF-?v?fM@A}$5 z?Rn{>_(2RL6Mcx0t=qiDSn`YWn8mgSC@EcC=za{GMXc7dVZaTyzS zK|PKg_&v7lc?F0eaB)P8we6;G_@#pKXuEyYZuqU`@><#T$mf;s*`9WsGNlaqaj|T4 zEG?g2kskltr}XU;ZFJo6Vb=LPDcfA(wl>1+fkICvbN33e|>K<{9 z&vv=p)HiU-!}PLib4Y}dcwHA$WTURFA*1XQ*4QgxbmO%Vz}t7x8&Tc z_-y7&d$@6|5zB)L>1+X>J|Zqy$792ROeY=KK*xPK4Ugghs+oaw|EC_s zx4?0Z<8L=^xx{$95j) zczmtq(uN8TCul;c`PKk#16(hRQpnAO)ZSRYAt^ZGz3sUCO+%_+qcITmb}yb|RqLV+ zzviQdzYZMzgbm}j>}p*jp5YGM`o=W7xBq@}%LWl&p1_00!z+f;1D|*p1H(Q+!`bUV z+9~HMVnE)|7l1dY2}C<6<@x3{9SLLju@Acn-@v$_taz%RN;~%dE-)e=gB-@I>I&#h{2kXN9gb|}? zVAAOx1-_fmP5mqG{nd6&zjV@!TXJs8 zQ<}c!ZE0vr|2}-ae0V4gOpK++zx;J|;p@gr`JILDrc`P{nWhBP(R ze?Pf~3Ts-nd^9Z|oJmhV`dO5T4~@C&#~y~kN=Eq zjA+DFKCV}^8kgx<)6PlM|>q$5B5X*&E5 zxB;#HvLL0eS?0B(E>8wklj2YXZR33OGA`>|wjK7B#k_Ik$E-PS;U{Q2p5zrS8yqbk z*i2k_Nt(R+y4q!5@{_w(q^BNyEdBichb;f7hf@%hMcbYCkTOnrJS!W?9S~y>)Evb& z1$)|QE;3?d?v(o4XNTU@W)pnaqFwt6P~;Qw<&!|4Lmo8rwXfB9W%c@-`uAH;UaGWd zW^;PviHFm-AO0q~S&akBfeu$?F_Vx%8G13Ld}dP_6359z)(9De;U`mrXFswtWAj5B z`D4ZfEPLu)U#YT;I>j{Ne$1cnm2K^ZW`QDq_Rg!*rThEuxBk?PYO!+1>h#z{kERE| z`hfKhovz;yV0~s$_|emJDrQwV`&i1V7%uel`Uz~?PaYum1%2BO<0&7{DdOc^-6rI; zr><+B)P*1VbzI0~>-KgP!!Q?Jvn5?~(@x;~dz#xgoF04Rne^ayo>U#wJN1`MwrZ-D zmDS}!0N2&dj0+wpD`jlA)>aD+YF}&=x#1*f$qlDu+`iMU_LKdZ3Bb}aQ?OMWZ-?Bd zmg>Wr{ae$<({@wP*VFcW>(fI&{8{??J>NsW@Qp{+z`qG8p7mi}oCY7d7G-gG*}`nh zkFE*|*OUu~n0jcyDS5NUrH@(`JbhQb48#0F9(h*nE;(ABj&<8kU+%;EQ|EK;b!*d$ zZtmY3kFnAYZQ7ci`N>1+>2H3WS805PmKT-K#PvGO(m|HT25&py{;;%psj8aejRP)= zaScpvqxO-cL2&9CI={T8g*+ao^0@CLMvRw&}8->kNR_MuTma$==)UEL=94 z^9U=P`Oa80Eq{kJ+BLRv0hWD@spM@EwToI<_T`!@PE0r5aAuhGEnTy9B>n86XVUk+ z_7pa8R00nJ>E}uKFoN2k{$PS0@Zkk~A5h~^3>L&?k_2U4jZNkzHd7A>q8wL13`ds4*A2DRF8w~DC z$1Ub$*uZsAKfX0_bziKXazj^*`AFJy$)U9C`j;bUe@|1}$I=rIKAnDg|6_Ucura{~ zfbE{V-gp2io(~aVTj@F**!Gca40OE7uN(iBjzL4->9bcX%IDK{%kSuT%f)QPD`&KW zR(Y?y^rE!-y8gfZJGOI2diuu?rpNBT-v*dt7$<+CsGG~I4m{V!I-!}o&$@F7o~K8Y zp)HK4w~1lfPupT2BUx9Q;)~95ERWxi)p06cI$evlVwYSPKj#w|P|D<6KEaCC@t39X z)33xe;QoE{Or;6^!FY>sp1japgh~PhZ01&6GUlP)5aV!Qq1{n0l8@#lPDLa7p?RLGkqki7BOKSoI0!w zrObNBckkYw_|}7kGL2t%eHz%*?HkI15$&7Bge4|HTW4#JtNfpdEm z*Kry(xDOi}3JHjyLb{r#%w`1^&O}0Go4}cG>hLEZAo$~dVANDB(+Ot)VTQKtNYk&q zg&kZF)2?@&o+kILL|;?{EDI$Uaa;DFV-;22{DX(3hT0r34QS;ztrcM zoIEsSriX~yh#NMFRrw4(<#Jt93Lut^7%nKFXYu2>VZ*l`ER^YnlW$5}S8P+?VvN}5 zjykFi(pY^|Dyxl4O)i|vS+}UA*H3k$jP{~pjE7KlmQ9UuERFQvw4op6Tl*uBW-^sg zBc4TS`ys^al$-l-FS6gfa*y76Am1=yW~=x)9XTXDPLTY@ambZ z>g<@{NipYZjcDkic0+HMH{{Fp!jl&+^q`VCWlpZQ&k`nAZp2l3;cyT$>iKw|a&DF0 z>JV`}55~g0ZSPEa-Ch0rjR$n0F`f5{m1))H5v#50#@cn8t9n%H1B=oX&w6H^*S@wx z7Sdy%fy-$Y5Rr!`^FP=sWH9j zw_lQWpMvv0)eRTfs6}n;DveQ-vj3I5g(-MCke6ei#I~nnwFRzc4#$#bU>N^cW2Ura z|73dQd$zTc?%VeCtKX0Yx8gx6>rTjpwbF1p0+&I^QU9Wb?yHOaK zUG`h~spEFb%Vpu0zOujh@bF6;;hoQI$iv@!bngjk(mUUJNxO`F+kWvI=hC_zBl;FQ zzsbfgy~djl_|0~ku^s4-Zw83B15g4UMnzKeCTr<|4SVBEFW;&^eNkh+jdvT`Di);K zkLx@Sy(xhnvfex~CS~C{aG{l+7>``*O%`#Eq&*n_yYSYFg))sEcxf6~!RtS^f8&ie z@fD?{91l<}ZadZ7y^VT0rh{^KKL7Ew?qAK%?AVn~ ze8cSvW%$#+J2s{1-Q%b~$Jpj_E)e6+?n2p^v7@Nl`7TczhjyjY8wA|0@GW22Z!dY5 z7}@3*(=AISR=njv{aaS#Y>(~QmNwjaGy1+Trj2*L4R3JbgVd;7SpjnUh~9$0F@)$? zXO@OoPQ9pUZP^Q>JBIF~XKWnL+d6vf>C-|smUWz7w#2~a(55|U>gu;P%=@`9d&Na* zWWyRfZ-cX4jDMlQQ;z*L`mb5XNUSQ+7AmeT?)E1=- zj33L(u@H5WEGWh>V_b$^XO`h@1$<+{yzOjnxQH1vY^@!>gU)9Yn4nc2t(c7*scYpu zjO~B!74K-&aH;Y9RDhrEm!<_)f#3bt|2)miOxs>Ydm46-yB{towjGmNwVukB-mD~b zW3#6t+o73}aB?mO<~kz`X~B!Hu(utg9NLaCL(F05nQEj2cJBD)O2#Q)C^u3PYRF8?BViieN$N zB86%%q<26!T*!oTu-}CYQ4UX>z8apFw^)MX6iZ2LfDd0;4~KiA9FZ94qaSlalU{kMG9 zv210rILt-5pp~CGkFb&dz%U1&vmfZjCx_Fm?_D@Io?{)p@P<`s1g~L@TF+1I2A^T< z2Hmmao8vjZUA2C3TsR&+KHX`_i#IO2V~*~lu|Bop4Gs^dod-{evMm^T$AA0MG&O_Y ze@2b4iwP;2QcIog!RH`mRlw_Pqh#wosyUNDSjz6?|4)Df%7 z4Vg1fEIgmk!57ETeTMH2%3SJvJkOjT+T+th>Be8*v0%nMI-YAAXEhj+%pPYyEer5oE3O`lzYj+(g z_6vl&`mJ+mWPA{tJ}Tar{UIJ+$k!h_jN*{j4uZTFtJbTS94Oj3l@)o~W_#0TOr@7! z$5{-<4%_e&F)S~|lTA~v6~*&Vp6-Wy@#(%GQ$EyHddeno{`cNHFNJ(zO(Q41JS`u_ z@!!AY8@~e`Z5aTq9Q)9Bs~OIixY2pdy1i+D z;BIo0;l`!(#MCn!R_&T^;%b}Vd;9EeJ0HGNSNEqgFT)Q`jHiva-?re!fAf3aod$7j zTR%C0@!`Pp*jws=bwTI&aZX)Rsi3Yf@wNb-w$ZNr76G>N42({(Dj9uc9}$bLf;U=n zYU$dxY6Ii>*c%V3c+eM_QqKFr*atG!^6|+uechc4Zv0nVb7>kF9kw%Y>?`^63M`dB zev5+G81Z>tSD4J@p{FimYF+S^eT~!cow_c#ETisN^))W|+CF5he2a8ZjRTe~aR zG;Hakwhf>ENVmyWo6j`ejsL>1P*y8CCx$j=6H>Y}&hLclyIW`u`M(`y1|` zdBD|dQY}Ml4L=F!rk4-s&680%1(@_0^#mGR24>566c&x~gh<}h2e$5x!5l0j&y+sG z+zibrF#pZF-=;c2y`ZM$6BB7*-TL(87d~hGQdzt+hP_E?K8sJC0Y~u4<}!vWz@Sj8bQ&Wmm;Be^m^(k({_zyhi-2=Xl2YQu*qL z6aH$v=Nlb>`Az5kX4<-H;e6}eYM9dK%vf5veO3DEU)`@Nt9;l1?OUHVY#gaPS}aXV zZM!0fFgkAk&cwVidg;Bq<*fme((28@k zeCWAt_xj)2owl7cskjSiv3zP8-{{(s9{u=-ZH4EJB7W;b8$!7%7?&hj8yg3s+<#mB ziRBGTZ#yk*=s9j3a>MIw1)l|&$#1{^s&rugdI}fRG(Izs)@&V0U--+%P$PWr7Z+~% za|?U}0&hU^tpTLvFTW!Ib)XdOXQ|J2;g$A?6Mj1M9!L3rpHI*A!~B#yEqCunhaU1C zd1O22f9uz;NC)udyM;C_8^(=?sdee_55Az|Di4+R(3Q%fw+V2g;(B2e^#{*44RBlG z+Xgr!My*YieN&Mps3MOioI@V6XxF-$Z{wnk_~zH+ggpF3JL4EXY`Wx8>l*Qk|I_Yz zcbeU^XQ2&$8lD+UliNqr_dfWLY7{qawAuSx3-tM&3bgHw1t_%mprJz>xWMya1h#b} z+o|bIUT|U81D|8p@p$BK&l%0{xKUR1D2;%6o|52%c;WIvuv>7zj#t>Qk&m+aZJoZPZz99=A0^Cyce*`mi@&z|da7iKlIo=9rAvMW6A?2fjc{ z{K}u}m3QSQ`;WLsi9`RpcHf?+CU-FF1vM=jn8L${o6<9n{*4|sV41i%YumuCfX8hf zxNW!Kw9%&CaT)*Iw|TCCu=uG6QZS4alsV>h$@RI+Yt79cNF&=8-u2&pS(%p}KIoT` zdX{-^+qNZb-n=P&@Gm|DOCE%E>5Y3JKW@9skJ{QX5$Srv87AzRf^fbgqet`WILdj% zsPyvVxR7V+a3K4y^hfGKY1OAM3Gr#Ea$cfRfRbly4r|2vNylWx<%+^RIN za%FnzzI&mz+Omv!Vy(TQdSyk6aa@gUA#7YA?B&O7HHsx>IINt7$kr)Sgsg&6aC^Uc6{mPl|O($=@C^QR>PH$L&8>`dl z8=v@l6+<6CRTNdsih+V;xr)OAQIC(TpwwB#cm=?8SuR}Yd--vB_z4^O4*wx@OwK3d zP9?AXP>wnK2^}frpwwA^I`h2k*G@@iT(l1TTA0(!>Y+5XawvWCp2yU|IHrZFod zxm+(K@K!x6`5*S?q~M`6Rg=ovOf?qtB|o;HRl}~}Jzm7=VJAFYGI+!ZpPsw9aHPw2 zUv_d@wQJ#y|N3onY0c)@^yxppU$wE`W6A!{H)Go;mNzS`rr7p{Z_4?QfME=mdD+vZ z*4MoI)?n4PCeN|v$x#@~(sSUzcluI4tNzqE-S`_j(Ic|K*iD~-mHLzWn)B3sbw03qZ{mqwt$SsZ6c6};G^^iy(4balI z^@g^TX`-L(tCBf;?8JO$eA{oM-NL$&Z zgTd*>7Vi%UwGw(4tL^77 z>({5Be(b{-d~H~$2hXv&9&je_v)!4r(<;i)7Dm+D#IWtBal*KaV6&;I#~>!+RJ0+Z z-wq#0o3TQ!*zUi<%YBaTcUe37z++MFFMLj4_s%r3|NMoy@uOD*D_5j}*_m|szOSH+ zeDDDKJ=B0T)p41$=BdN1HyAiBzs(?Um3^(NZR9&}5u>Kmg+6Fo-;Q@Qz5A(iYUEMH z3CxZiuS+Xeo`z%==Co{ZO>xoa;?(S-4_FU<#K`t!MQ}Z1-IRhg630#`tpL#(m3h4B!Z%*SE z9;DHNo+ft9rs3Jq^w1~1i+*~o)Z^f7Jt(@ST;rB2RCE++y?{?`pRBj7D4E)J+q5xP zCgbhFG6i2K-F3^$Y$ES27|&zAyf)wY;*Ba&E_}G`3_LD zZ#-dD>S=D{a2mr~5Wan{J%CW_=I3qwXkgkR+p>)h^8f%q07*naRKYvM)(zG&47mKKG%op~cSg z+}SDH;QcIGy!0)c9S{mG>}y{&Z#T0UiZ{BAis(ug#JUMgiCtj|e?jHTbIUxl^t_*a zKGxrC2R|==*S2)_HLID#f}V!9Y{NHTXVPPzxf}hAD|p-w7Cy#-)a0y}>AA10^}**i z8n|A^4UQ|X?4iC{uj2;g@B@7lSGTJ;!dZNdzvWfwbDqSs<uYRt(bzfR>$p+Xj z>}lKjsWdY&oIZWu5B-4411HYvMU3ZxI6eo8R=Q@}aSc7)$$<*l#CseDumg6E6E@1j z5@1>_eYD;GQS`-E*hOy9E`IKO%f;!c%TH9!3wg0_$55JBF_7;2#FN-m_-U@on*&@b zy}lVqT>SCMuUV0blb@zOT1DT*E_~9SU%6}f2_KZt`(}ot>F3Fly=On({+5flwN4}AO)9G1CF+6{W6 zY@=AbfzP{8*sf~1$*8L^H4l7Xntp3PB_91Z@Yz@8U%8ll2R`T5%YVtWnUD9oinsoj zo6_vTi_zbOJ&owFq~P|8hD0C=84L1>gL0Y;2kyO+Gg z3cH|kD@Pea5g$^hs7^tvcFv7%2<0O4Y+AdwykMrTdP^ER?ZphYpr^rgtJ8A)w8-K6 zzh(~vqD1*2z#eY~(1U)&QP1l1z;XN3HKU{G?ad12kK>i&yFJHQk+Pq_aKAv_+9eNf zhdg|0eT66bg{2gi3#Y2WsTHV?iLhdyKr%2-f|hVPEH*=9^_>*p1gXP3CoQa|^yx237GuRuae z(=W3EOAjCXGOPbQ#6J0?18LQ&x%8nAf1LYY9i(}n=CPW4XdSe(yvXcW9(t#+0cW=K zk?kpyvOz;16z~o`ohctFUGMosewHI~^XTQr^niIBMP|ceBs_TV`gG;xm$IXaVjA4I z$sRzs9}gh-#QJbjos1Q45*N6htqxo`aA=2Ka!4H~;!PL1r4f!2Coc2)C`oe>w!%pj zmYuj7>rj#D*StQBA6)n!Jfk9$d*}23!cRW&4_@D>vk#9K%C;@m-)4ein>Idl+?L=y za12DAq@omaqMVcT6_t=}2h_F_RdQmbhb(9~lzkiJbml#Z=o2tl*A%f zHSoE^`H+zk6LL`TF00=p(7;so*?-9DSS|~n%%k>`2b%FX*S6$CMn9Zg7Jf3m(&v2K z*G=y@kS=-2mPIl4?Bwb#_y*Jz9zgig)EF)VrRZHa+lBG7lv#^uH`SIa1 z`~z*n=K0WC|3Qn-(g!(dfxed*6?$KJv;RCMUt$HCqZXLxVa`nlTpM}kC+%uFWS1wQY{mtW6 zF%$=vf~`R0y;zj#qP#h~A;xptm zEf@@R6~AB@s}(QDdN%UXpW+*`=$FUdaLa4c%F7nd_22Qm`|$w6T>9DHd>r+!&#Qx% z7%J?PL!aM7fNg_vy=ee!?V zho7JoCfm>BC;P7LBX;ephrZ4u=iS9VaD4D!&6*3*xy3Op8{UR-oJ#32Jb-}XpJpC& zgXh7r(XMsRl5hK|+vK~nv0E-Vwe_JRt#!5hS@L@D;Jj<#aB2EwS77PkgJ1RtJP+|t zK52j2v}r^7;GciUj?9XQgSN-^<1$))Aa}j1b^bvxEp(D%NFB5?H1q`4`W(dU8%x?$ zdUF_gF;>wVj$jHsLanDrib_w3u%@}|?QcpiK6EjGi)!Kn2m`aT>B)P)s0!)B8!Mns zX%%%D_vbny7}J5{1ppp2NDUgaX(zo@p>I;=!QrtqTqhTuxJDet&2WAg<@g%yhGQO# z!=G9hzu;m)7SnK3?sZY&Z3^F1BoCeV6$BsfESx#sk)&p7;R59Dah~^WXbh)xYSxcD^09 zYnkf}7(dYGQfH>M9@&NnT*w)zb9{V*Z|FE&8A}t7m&dc#!>907JgI30oy>Dw2#LeQ zpITT9Z#+QOPSkRW&a3bR>C8FL~IzU%m0b z=Sl0+>dh0qrj!Q+>eltV#qu>nNe&a#t zH@x{knq35MJgCQ>JRd+M02GSQl{wS-4zw!nI zo@et-1bVo@wGDmXiOYX>CD_{TtK~=IQ^g4^NvH&j#GGYh^si@l~44e%BM>n z&(IFm{HcY-@Wz9B?8)l^gpn2L=?A`~ZOqqhI|)Cvuo&KW&>sJpedB3t!*F`wqd&&V zz)x2w7Pgb19OQT_fj&J|Zy<;_CUoF|ksbUrsmVL=ipe;@w{vLZLwn@b;lI1Q_SMLv zBi@n3pIVqbcu~7N3%<<<5Qf)pNRRyWhy45=L%>rdjD}9b$l)fvB$88rGiHjo;JB!IV06LG2+Lbz08x`h1c*d+_WV!l4p zrVpDM%@~|~$c@*sxyW(AWIvfHvW%7u;vIkf1jE$6Rp}?6{BAn( zFhX8b$-$TX1C88`Q(@f~eRk+2berJA*5C~rLL#4tFQ2fn{9I2T zK8NkS^Nm70j=t3?z_z%8x%S8Z^) zlu91DHW6(kJ^6;eW&N3e-@vO};MJYQCxGn7X)AiK=OtgpYnxs8JchbJag+S66X?g` zr?vqOg3D;r(qc)G4^0muECM(6#crreh#pp28r3*Jz>n1O-mH(cC+%5U4!t`dZvXkr zmk!waY%mh(bu?fqAlb zZmmk-0WYHa4E&(M#?3@ng`xYy2`e(?V_SaCzLrRi*cAP4-JWUf2jfeLHahJsy68_o zr|&}^WM@)tT?-)6N^jWHW#^@1Zl6Gc9e5w_QUjVb*%~I4RK1reOOK;3^kQ#oGN}~u zlCc?#$pkV`Ku)dW+8Vf-#_an}?HtVuICrdR2!C(Kc@2u4d6OL9FkO^qhdRfifEtlr z(i>(b@Z zgL?d)$Nly33b;XA-{eS805LKduzCTD24a3B8L3 zSb1>h>Z9eOEnP5svYVjYk&`O9q&G6v^8aG*t|V=jIutw4Rs+7tLgT5k@0+VAWX>#I zhe){%eIpXl?bjwt<4}>ac)8{`V7)jajQXxEq67AUcc91t*RKW0mh=~+LLSt7x3YxO zlkQv`w%h$R=+DIFH7MI;jY_}2Dtxj?Yk(?76*Se4hGAJ#9 z1oQq!SNX{`l8?_52VoXqC5uVu*R=Fq-M0ZUxYw+1w+udy_zy3wc%EDmc${-h0}#G- z3#rKHI7$1fdDBV20pTNdKJ4N?8*HN1fh{-h`L6CwfDAR9W!2oB1#!HqO6gjD`XTJ! zI{UMv{{^QbY>{%^T2+Ut*Y+;y>e?bl<&y`+v(&6ra-@|D`Ib7jEZih0_fF0d@$Fss z5ecLD%U(_A#v|;;WAWSm_lq3Z@et%9wrs;>6Dv$!GT}^EKB(U>(zN-R!4$STvr4?g zR3gxIc8)qySM5=m=z9p;aDy_hGf2dn^k}waV4b5Cvs0! zH-** z88_}TDVd-gi+;iQ2IPN2j0g_?__Y$vPwKRI7XFpRcF{ae2&f)R8Q7cDDNetTC!+Br z229lq!`m5O{kWv_xedhmt+pZ3znt%xOf9M?s&?8-RjB}Fl#h`bHz4ItgalzDpwz=Z zPQZKJ!R)7_q^azUrYF6rr2{SI;rfFu35ke2L#011t96J2{Qf9KLp6(WK-}R1q_KUM z-9F%aE%1nfq|pBJ`lgiE7#Zqkb-<0IV%>%ZZY@7xfjs;8dkCcf&woZUbRrk+Yay)F zIS@1A8d8m1_Pt^wvWy0R12TePl7`74VytEBXWvaLZS3tPAyOW_}vz_}qM@cRpRTplE>qB_6&r2Gb@F!BW&M535 zx{>&a|FvciwS)Z!C&vGXdDRo(i!j1hYuN-Yr5{(CWM{|DDi|fAR|4VvWH*hRp!Kf= z_1{_<=_gIhtZrOc6C?Ku-U-JsrdwvMoNX>+1kD5lPV<#>zlGq7M(uwQ z>N5-xd}V=2rwkqwFCMSAuGOyKvT|A5I@Uf1dOxXl-+6TO_D7G|)8})3kRhfuG$o(} zmF?ojsa*u?o&}%NqsYP6HcY8MkBEOa`wabvISrAjk#^ou6kjrGu(%WXK=^S-e;MvE z+kF=BUp>U{N%Og?)=e_|VMQkRp{sg5$RN{==X(y(6ciK9I9g#Onk zOFV#6+w#Joos8U|3Hl;0$8ak4cvqc+DG@D6S^5DUItviWh!^}PAMl=AZmA(>%`Qek z&-afAsEWJ(aIOFNr?lWz2=1FPP_Mep5v5M9k*^$h4G7BVydKnWQbY>6+HDnAKH+z= z{U8M4gY=y&x*fG-We{lEQw$-0UyhNPZe>h_C(o94q~|`}_;1j=2;G)NUDz=H*9vCE zt6wYPl`M_L2kpNJrnsZj^`7_+=D;6lvCnqDJ6=fNt3@bq4r`j;>y0i)nQR#itMIS}y1+i!bjv3sEl?UIL)Y zu9%(a~`pv~=J8O!Oh{dlt+3x$Q>)WzUH-A6Ywm5i7vE1aa z4>NsOqOKO|&dbE-TC4zzwEy6~%8oS|jOVzO^hqDWMw^`SdSh<{;PtQwfV`&rjFuSl zn~Y;IvmvaNGkvx*3m-zl&!O|$%mrb%cX{2{OBXI{X^tN7KbxSO3^xV7glHSI-9lV( z61+g$T^Fa|9Fae$*0EVXY*FSGx^MSdVK_=7jBtx6%s{OCl>b&1h3Ot2EkvuS;>Vu~ zWVnzwse$E2W~rcRE;l1^}-xc1zx zEsbvgwX*W3CHsmKilI8up}gIExbMzogzpztd`_#%+8m$Yu$r4GHd4RUVc*Ywv5_?s z`QMt7L|h>zSIJdlc?#1OycAE@WqbyeT&(S4z3q2M#=k!Un#-;*xD~b>+e*rwp<;+iv%D6zY0%26Z<>s zs=4WK#bh#_UG!^8alxM*rv2$W<3{k^${zJ}=NitN$YwP?w!Ob{mbN%ST^&&7ZvMg3 zC>Z$ky-J&eGR~_#9QCL@T*Kd|A0_zoUINX-m*2!gHl*@n9fBf{Nf}%6?A>0lBfjb z2j&MrPCD*J;*pd^fqsLFqfa&>Bc_vYd+K8w(%?li%#O+^_0qm7s zC?&uDTKMmI`%MC@0A1`*!hxmusv+4wTE~Vf9Z`Khi8SMd+Sog}3wE51 zk)X1&y@w7b3hHi|(!>@IriP*XEqmc&O33~Ly$26%Oex)c9_*?w?ELA2i!b zBF^#NB_d5EVCRSmwH%BX3A1CrI>~7-4Q6?iv%?E3+#3K$|@PTQdAZq84z^M^KfrR9@p&SHTJNwy_FtY3x- zG-;NG(I58@nhJS?>fN7~f?l3LuiskMeBn_Oa!F9K8;U6sqf$*?@AcjOXVr=dmn<{S zYJF!#n5VZ#VtX*tZ~~iZuV2RWBy=ZI2;Rz^RIH@(+ESUeJDaxIfxVfgV?6=&kp#wz zF}|waYw46aO-dN9P};Vk+XDNnxVha>x$Yl2&a_easY}&+13$>9+>GA8T@-VBc#B(v`$|9o1b0)M37I_23p2SE4 zwdt8UqwoFC{I0Yp3PUqB^#ixEQOws=Lj7v#U|+V)PNIgH2DY1pudiAaLDy`4pG`~| z6OydFloCNb(pd%Ue~-<^yih`wPgkqwfTm=BxmVp$tQDo z@#fa31oMh*kMS`8FI!*6YV+u_cZrfM+#LEm^lnpo?Tu#M?2Rt7HeE8-~P$YUI9b~@!LqkY(w#Gpkohk1t5)$ zlAX(Lh$Nh!Ez40{VzGS)VV{-+;4() z5;K=`ZW}dXOpL$&rGam1o9qBbMT`e9>}}G^sM#KW9PPU9CeH~+)QK);Sh!t5>OZ-Bg{74gzWeuAgQm2Nu#t~xfL~KhWA@91Y`*=> zJ^u7{x53-$`m=zI4g6ld$kDva=ayGhPwcZ2%tLlet42Q24f@h--J6-)*GTBPD zicx}$7k7e!RteY+{UZJALF2DZN)PjLD_>Hq4^(_r7`~6B$p9BMD2?qt8h*f2SDj`) z+&O7!mWzRU9aW^pDP^{7`CyS16Ai$=FvE&)LG!Hmw;{a%0r;zBqc-bI-JCv@^TYo=L85wskEiOYR&VM%q7H#txn9qmo}V<9uZ+GM6#a4)S5i zF<&5X+H4o&(Yy(z2tu#_WP}QieF|{(7M~o=O$zPw^ngA=!V+cku21#~T4Wa|lweF3o@*JG?n@2Cucul8(VNe_ zJfO#YtoOb;s(c+cfD4Ua(g{PoRP#5cS~+KYB0IOLddd=X^cB-*I%dK`D2(<00rk61 z!koa@O=}R{O;#;cUP_+9ch}D!G{qukw*B;D9$=)k~jgo z)Y)?QgHz#0z}xtJW1rlotiI_HDjW)tjYykL5LRQ4aSNI|vwU=yjbJ=7yU&+v{13%R z5L7<8A{c-+cnOTw5nxDGl)D;)k`@vjHRF85Ef7z5=eQ})>~}ZD3`+7))~udV_-sC& z2VAEXRQK#Rb<3wG+W)CiB}cgE4woAJabGv3Cqk}X_5Lc+-mr<~ z)H?vqZIMBr*^{knW|RN}kc;_JGMzCa)H&sb$^9_D-dOxGX~D+U`wjmm?LPsp{P{#9 z`-U%??0KO`@KLxf?3zJC+gx)G3x5=Jxw6vyN=p%-HD3TdRokK)NZBF_K`}zzl}5VN z;5YQ>C<9U5I^*TE+a>8R0*CV+cph6E0SKDsbu|IQq-%2No3eyp~F$571d{4SjB*i!U{kkZ!*5=Px484HgCG^;I zO{wem&+D9lMkk?r&QUU=nnk{dTX#aa4zA|@{3OaK-$5whdECc8qY9i%xsWDWyc zY7=Kfkc;XUIf%RweRELWe6}{5JYl8!_FM?;Oud7*2Wa_Jd+l7ei{9E&(Eryv-1Y40 zhvN2jZ`@-F9e+3*Pl+b$eOE6KaT-9qj+a-TWglNwZg!=qPG_pPddiQ)M! z#sG!Gd4}Zu@R{>!-6M{7?9~;g^&e5vugaAkiMm_r4!7WDN!*k8-_scW5OTiKb%<+K&C`D~D!<&oO=5zm8Srh!9y{U+2I zuk=OUkNkU%E0^5mO(*tDfQgc;c<?uRwpa4+7ukEAh2?{AaG)Gv*39fXWs&(S&Ep7Ks)VKW)Es=&# zBnYCtPgJ{r>u$r81+V=+3BtbJ!MXYcm{*i^-dBroU3LGN)GiD(9;QD__M$_YoI1Wf zI!18YF4IU4BUSF`SagN{g%e`mTb5FaB;xHnRD4=doCCc*#5)2^m0YX|!>1|Sfr>8u zyW42BS#uS`1Lw=B(3|Wm_1QNW05~K=v6tV5-*m0%rldKy!hw2Td}4~ebUz!<#T93E z#!lQXPPhEErK<%jEnyK`{{pJk!Hg#5$dgU;ElsPf$QY2{SDEeOBMV+fGM_by8&&xkfqr<-je>+KIY`GMF6=)(i-B}UTsIteBcmj1KGU7;&Xxwrl-Pe^k zD40&?KNBj2kxGM4c*<8cUzC<6>w$`pM0gAmC{DY7PmD@9(-%0P}1 zW@2{xabR+A_@ZXBg~Qd`6VNJm7m}=DuIV6Ru4PlWq9*}Ek%L`k40wDP%DKZ5W1vqX zL%Zi)H(0#DGsS+O-yuAyla*vhDMPW*YOFWAt6dH%uhz`l+BdP-PX5wnzXNbU$$aUQe~2;lI|+8DU*v zWBa`HBF@K915p=Q70NOro1}J^A(yASnidKCH5;5!{dt!T*aHSooJ~p`3j*B zRW^qnFlTf00X~wg5&599>)?6rciA3uq|vBTB<#tc_qEqX?%tC2!QS*V!>Y!i)1O74 zisRzHo8HslU;s{BEoIktKn8~{1<2dZ^(NcXB7E*f zxru>kWEZ~Y%O2R;8f2N%5cd?bBOVJ$UnfM8axdzV<~x=7KFncavIlqiAE}iuP2Yff zj?R{n0T{?PHpa<;ewW?7-#0eNsc*iGTJk+$SJv8b-T)b3`e1S=N z+S*sn3+Zw#x?57Ze)7~3TSEE3RibU@q=({@0CzURW#{am2x?#+Khy=HMpk!Ti`^U$ zK`%?_}n(oAvj4$=&k)jDE@`NM*Wem}l{g zL641E09az!F{x|Id_$Q~qSaEQ$pylbzSQUJ!OZ1d0$s#H*d|9y2kn)(xHvxlL%NU5 zT1PIPC03gyeIK~?s51DU-+VegJlhMeE6 zVhmw@8W@rk&)?{0}N|eZvzv@3lJq=KJnpJ|r@MwXWZ~DL?i2As5>FpbFRWkce>7m}-D)kO(kJE0Y zKF@k$?DnigUKDO=7*&1p$6=0`fbnwH>tfh80;7U@=!t57tBUC3-<`xjyc=_O80 zf5lhskbS)df%yktSY3{vRkj+@XMU&>cIk$dG%rvXk6p?c0TS>}Bop%QO z|GYg_`^VQ3f3%{#{S8@mnjFSl6fY=%>7WLJ@mAs3KA!gLj@#|^U8~$+k1T^*=?U@w zzF$-O=xdI#pPQFMz6brl$(Ajh>dNL&?p8E5GW<>dLRY=zY?nFMz-9L)?Kaj3p4!$r z_VSG(2TSAh5GPPUXf>iE;CO}a8pV|U*KKt9E6vOOyOuJ=@*X7tJJdG9S%(c21xS{! zg3YXO5zZ-Np^%P;ZL99fdV+_iyGBe_hk%`$D|W0UT6NC`(=8r*jS$ya{?7GWx${ck zzMa^s#Pc$;!c6dsFQ6UCEugOX;dDU3la?h^>228`h~!hV!0BO=H?g&|EJz@-7r#?4 zK#D&~-@RDw`BLv4JLhW&=yfqGo&ESdYO6tRz%jqlafTuExM;V{f1nC>^=hsmpoU#q zI5|I#8hNpj&5Ax_rBZnP5a54#Lar=#p(eWkeEz%Qn0IT#l{OeV`;9zf3N@$uhs*At z$no>I=dNKw$tL!T@f%W%{^(&y|HQzBSl{-~x)VgF9Z&tVO5(5hI;?G4qB z#a;7I@53ptefWn>8x>L5R1hIGlzwxnC0n_vBh*Hy<~nuu=NjW}cBSasi)b}T1=S0> zzebsaq@WCn+ckD2aOm}uMh@p$V1X6_!~ZDP>D9^8_nk#QxV zozd%uuQQaCz5U^7%S~2ib(YTOZL)T*O3`%FF(tJ2(%IG6MMNydFp3P zYL3WRrp)oxnOywe7$~pIlymR3?@=%$ zeNelBY4<-48V%0Jxn%X2HH z=lvBYQj>yQO!0BGb@EA@!r!;@Is(WBCExuXWs3=kXd&pxZfs~E2P&TDlM8FX@rhy71F>bGZm8uqsn~PM7zRp{@Zyp&+cxHY$G)YpU=7n;|T!AeI&TBG8 zGg;@cY{OGyQL*Azf%3+1R0g|=S5`ijUxwJd(M4lrOnG2+myvY5(DHUdeZhIJ&;z9@ z9#%<>hsB%vkD62*+L9}+m*tl%%t(%?d$9GRW*H$Htqx<$(Nod_Ld$LU84=3^vqFO3 z$=;9oaP)7<0@lp>ZEG7C2(M=N`2yTt9W1DGQCxyu6yMZ!;(XPkrlgs2D_ye@%}%lq z$AHvo!^%TZ|v4|sQ{kkKtu@XKD z2&E3ix-F^&?Lt261q8}B?yc!+s2vZaXEPPpsfb56QXWHVcHZnqq_wKB0QkjbwM`QF zF}DKCtR=FYJcWCAu%LnxKS`kdFC#g@d>3f73?KuHUTgS8#;B& zw2BW%P%Bw0N6QSx^r-0vxoXymym_EN)^Gm8EN82hWKRJDK=PvZM zl^TV+dXI9?)6bw}HHEr#fl{@WrFX8#GEi1JJH^hTw@rt`?#YkE4uIj}1!(%4T|Tbg zC4cLw>tZC3;D!tc8v{$KH&#H{L*#LFm2B$#kqkzqzU%%iNuz!_QqC-$n(m zW@s2xoBX(MEPq$r08@ncaXR20?%5{6OR1z+!hF)L9X|x6p8Wi$N+8`|xs)E3UTK@! zq5S5ohmxhO(=7QCJPyd0VCmWSMI*sCj0OGXvQx0no% zIk{UVXX#UX=jZR}yeG315F7^W*l%dns2!X!)zsI&qjop3x@CZSLlxjo9Ts6KRzRPG z{v$uQ{0#GLB{S+Bj!i1OXzB_vxV5wWGtYJSZi?o(+PwB|qT!n#T4wh%x(9x9={0et za(#vWUGmp!Jir}bt1tDhy5uo>K>%-s?emqFa=ML$*rvblI+$5^d_GPd1M}q)4m|;a zB6boBs_`)qE^8A>rp-Ymrj5eN7E1xuvpl{nsBGE5nh@1b7gk{{$c1PO6?!V|uG74E zjPupb>kmn7d5COQ57Gi&Njqi5@NBxT<9vtTynAJGV?6_x$!P2>P7l+{wQ`Rlfpgq*?P@TrF{|+(A!56WoC-}s`mz?qdH+dqyD|pY|rq!}B zKN;&$DEHY-D@m)6`caLwu-!kCa9R-P4+a_+6iN82b;-(ZQjAc=f7+XjiG(zVqED*U z^zSH)Gu4!LJ(+ID8V#+LC1*n#m<05d9;qGQFUoV)B$%y~fQ}lPVpQU9Coy2ga zxz%tV$Zou+bJ_9r8;d;F)!J^-aBQjT^=nZ*j{V+Z$>Uc+ghJVSn6a7?H1suXND6#K zJ!qDTw5-cMnKb9XEULrbD28ytbdl}hp=HG{5&Ls<^4RBgQnqo`;qEBWZh>Z5ioHrR z@#@TBzU^rk2t*ONwJctMDYJduf|P%!+C?H?Or?NR_^>hclp*jKAV|{hVzuGacIrc@ zC!yp?>^uym2`KL;rGA#!zVh*VP)?DkUj>u?3k}TouzNXGzo>Nu{etMVyq(mIOro~k zOzdt{qXQ!Yf@qVHBgBK`l?bB#1-(-2)uvhP2Z!WyM|v;NR<{gi+WCwu_kCH7L~F9^ zzDT+w%@0~w&#mn5ZGmrnqRbk3u5r&^Il*r6MXZlG;Ko;v|GN-@goobw4Xx$xJ>$9| z3J~8d=iPN{sg?zogI2zL%*252CTjam6_( z!yIt*swSu|j)sPMy$-JT?>AGa+vXCPS(~@|ZE6MgM1&RQUu}?txL{ZEQaS%lwS!Z4 z9qX+1?q-~?%hmmq6b5izh)69^mX%q3Z^-8NLkjL0clOC#ckzN4i>MDQ1Xq(sOo!2P z!v=2^Pv$N)FfP1>F3hzvQrxDYG_|w9?ulPKb-fAwVcnP`(s2EHP1Z9*se#rm`Ha7^mmxKy~6?lga8)k=6M zn}zGcxj>xb-&IX1=-#v8OpbbsJ$2*ZNV)eQShJz&Q`d(zQ(MA@A=@bFaNs=)7r!de z+)wS9ZhyXT-!0Dd0H?Q3_o<=Mh*=iKhZcFqMd&L5_^8mDr= zs@3?i9X^b7p|Hj-qcJk?lMV6XV)l2{BG0^oN*vt%bSPq}_14yGmzzNabetJ{Iazjkzs?G}upy+l(AYEp;=5~6mUWxu>E ze4y%2vd#qjwB{gCvfz1bTlX!k6;kDQo<87cO0ml~)3CIQd?NZ{rB!P~+2C8K?zF_} z@Aa-=f2Xw7znVF%bKmOH+uYE@7E5-M#&z3+ra{iMRsz0FFn{)xmXDA}e4gvad9Vle zW^cmd^k+Yvgey3uI|hIMbUR0|q~C^_J%0698L^pyVsHgh#JIQhyY>>*1OCErjvryt zheNp1Ilbr<%|%_c2c~@g{r*5{zLaAw10?@d@t5V#=5i|qCsN=TAHGFk__p-x!$vyn`)(&cL#ieL{)qt2xdxkVf=m6!xMw_wCp(PABz+}0 z-si!lJh0cqcaDWUsjPZCxEk7xj!%O}qEiPf@Km*|kQWCID8l6)At9N-In#M#vp*&U zgzm@2y>LYie=+M?iEcFA-`?AM2?VO%jtI}Ijf&D785x<&;}sI3^lNDm{7!6RV)CfG z{PAmJ(=f*2^pA$?T2j)3nx(;n<(iDF$$hk&P7~t0#)+p@NfWswE--@;0goRndM*_` zKX~U(E)!vQ&*0_lw9mHIniid8jN8dn5|1AznV9pck)IBH=zROReZD>8T9rQYS$&If zDD7=AH%E$ObKb~P&ETQe43gH#8SDvzg~>UD^v^a{_AdMqlpcvDSrL0f&o-v+50lwG za%UAvL_AC$C6kXq?W&@$6z;|31vZl{^Ez*{?{+^eV&$dBJ;+XsnVNf0QX=qqMBW*v zYHiP7E9st?ZXW#X&SSwO0p=8>abdYDv%|yfyiQMP_d4HeOPx3e9y;~XcGk`|uLptq z)(^I$KQ#l{3e^oBFwJjVP?ePK)6p|$YA5mGRGY+1L+`pjEKmm6G+h3$Fp6C0<9T9f z5Ziuh#mP(u56#N>W7YpK+@D|kF@c+zIrd|~!|qM$^3sir*dC?G4-RT1*aN(lme^RFTSK0^2V9G za55t+qbHUPe|n3{V2m)ohPYmH%6vcc(%z8c@y^NX1|Cip&l)FZ?&G`6PPG{<(@vgr z$yFW4LH~#^4>Lag25E>36CTO)%0Bvnyik>VWUb1MkGdK1&!05)8spFs(I<(I2!jw4 z!^pkXPnoWd)m+rebY##MT^ZM$QGKqsO`;Er5#uY;(J;sx4B(}>lv2ESi_8OPEs^`#imiMK9j(+#Cn5T8fHX`t!lD9E3TsLze4>(w=(d*TbG3Kc#RSn*wB;8yk=hNYR_#zsl7V@2PV3!Fl5#Z* zYh{UJc|wzKyXZN6KQCSI!V~K*qn1aHPP%dC5@);VmzJ3%_viOBMhIi&1f;AarM8E= zO{}rtNmsE1`TMBYV-{u-5e=hJt>?9{`6Ha>HogO2CJMWQr<}<`u|Xr#E5TmDv2o= zJtU^%`d^pVx2ROC6#w_{-@N%MhA0d$@vDF6;nQ3H`*Kqa>;J3f{|(yzdS(8;L!rrkg<0w`ek`khOQ4=;qOUq4ym2%qsN%07a+6D*ylh literal 0 HcmV?d00001 diff --git a/docs/static/img/sign-up.png b/docs/static/img/sign-up.png new file mode 100644 index 0000000000000000000000000000000000000000..3ffdd179367fb98d469e83c11a889bb98c8bf0b2 GIT binary patch literal 68595 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 Jiyv8p{1;}orj-By literal 0 HcmV?d00001 From 2a3ba95af9ad99c0d5e5671cd469ed090ed4f3e7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 16:10:29 -0300 Subject: [PATCH 193/213] =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20update?= =?UTF-8?q?=20reference=20to=20settings.NEW=5FUSER=5FIS=5FACTIVE=20to=20se?= =?UTF-8?q?ttings.auth=5Fsettings.NEW=5FUSER=5FIS=5FACTIVE=20to=20fix=20in?= =?UTF-8?q?correct=20attribute=20access?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index ed85e148a..9541bb2d4 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -40,7 +40,7 @@ def add_user( new_user = User.from_orm(user) try: new_user.password = get_password_hash(user.password) - new_user.is_active = settings_service.settings.NEW_USER_IS_ACTIVE + new_user.is_active = settings_service.auth_settings.NEW_USER_IS_ACTIVE session.add(new_user) session.commit() session.refresh(new_user) From 3486248faa156f9e2720c107f8b4f34f9153ffd9 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 16:26:53 -0300 Subject: [PATCH 194/213] =?UTF-8?q?=F0=9F=90=B3=20chore(docker-compose.yml?= =?UTF-8?q?):=20update=20volume=20names=20for=20consistency=20and=20clarit?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename volume `./pgadmin` to `pgadmin-data` for better naming convention and clarity. - Rename volumes for RabbitMQ: - `./.docker/rabbitmq/etc/` to `rabbitmq_data` for consistency and clarity. - `./.docker/rabbitmq/data/` to `rabbitmq_data` for consistency and clarity. - `./.docker/rabbitmq/logs/` to `rabbitmq_log` for consistency and clarity. No functional changes, only volume name updates. --- deploy/docker-compose.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 770e0ef88..cae0a57a1 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -109,7 +109,7 @@ services: - ${TRAEFIK_PUBLIC_NETWORK?Variable not set} - default volumes: - - ./pgadmin:/var/lib/pgadmin + - pgadmin-data:/var/lib/pgadmin env_file: - .env deploy: @@ -200,9 +200,9 @@ services: - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER:-admin} - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS:-admin} volumes: - - ./.docker/rabbitmq/etc/:/etc/rabbitmq/ - - ./.docker/rabbitmq/data/:/var/lib/rabbitmq/ - - ./.docker/rabbitmq/logs/:/var/log/rabbitmq/ + - rabbitmq_data:/etc/rabbitmq/ + - rabbitmq_data:/var/lib/rabbitmq/ + - rabbitmq_log:/var/log/rabbitmq/ ports: - 5672:5672 - 15672:15672 @@ -244,6 +244,9 @@ services: volumes: grafana_data: app-db-data: + rabbitmq_data: + rabbitmq_log: + pgadmin-data: networks: traefik-public: From df81154536b04b014b23eef0f52e06b57ba5147d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 17:08:54 -0300 Subject: [PATCH 195/213] =?UTF-8?q?=F0=9F=9A=80=20chore(docker-compose.yml?= =?UTF-8?q?):=20add=20dependencies=20for=20the=20service=20to=20improve=20?= =?UTF-8?q?startup=20order=20and=20reliability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index cae0a57a1..4eda0dc37 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -73,6 +73,10 @@ services: build: context: ../ dockerfile: base.Dockerfile + depends_on: + - db + - broker + - result_backend env_file: - .env volumes: From cf5c7b817e7eb62a9ca904dbbebf1f3143514c61 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 17:47:59 -0300 Subject: [PATCH 196/213] =?UTF-8?q?=F0=9F=94=A7=20chore(docker-compose.ove?= =?UTF-8?q?rride.yml):=20add=20traefik=20labels=20to=20avoid=20conflicts?= =?UTF-8?q?=20with=20backend=20service=20=F0=9F=94=A7=20chore(docker-compo?= =?UTF-8?q?se.override.yml):=20configure=20traefik=20labels=20for=20tests?= =?UTF-8?q?=20service=20to=20route=20traffic=20and=20set=20load=20balancer?= =?UTF-8?q?=20port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.override.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/deploy/docker-compose.override.yml b/deploy/docker-compose.override.yml index 01e4e2a1a..829aae883 100644 --- a/deploy/docker-compose.override.yml +++ b/deploy/docker-compose.override.yml @@ -72,6 +72,13 @@ services: command: pytest -vv healthcheck: test: "exit 0" + # override deploy labels to avoid conflicts with the backend service + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-tests-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-tests.loadbalancer.server.port=7861 + networks: traefik-public: # For local dev, don't expect an external Traefik network From 4699027ffef42eafe7f19c017c8c7fbe14ef5e06 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 17:55:29 -0300 Subject: [PATCH 197/213] =?UTF-8?q?=F0=9F=94=80=20chore(docker-compose.ove?= =?UTF-8?q?rride.yml):=20remove=20unused=20'tests'=20service=20configurati?= =?UTF-8?q?on=20=F0=9F=94=A7=20chore(docker-compose.test.yml):=20add=20'te?= =?UTF-8?q?sts'=20service=20configuration=20to=20run=20pytest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/docker-compose.override.yml | 19 ------- deploy/docker-compose.test.yml | 85 ++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 deploy/docker-compose.test.yml diff --git a/deploy/docker-compose.override.yml b/deploy/docker-compose.override.yml index 829aae883..e5c0d9dd7 100644 --- a/deploy/docker-compose.override.yml +++ b/deploy/docker-compose.override.yml @@ -60,25 +60,6 @@ services: - traefik.http.routers.${STACK_NAME?Variable not set}-celeryworker-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) - traefik.http.services.${STACK_NAME?Variable not set}-celeryworker.loadbalancer.server.port=7860 - tests: - extends: - file: docker-compose.yml - service: backend - env_file: - - .env - build: - context: ../ - dockerfile: base.Dockerfile - command: pytest -vv - healthcheck: - test: "exit 0" - # override deploy labels to avoid conflicts with the backend service - labels: - - traefik.enable=true - - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} - - traefik.http.routers.${STACK_NAME?Variable not set}-tests-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) - - traefik.http.services.${STACK_NAME?Variable not set}-tests.loadbalancer.server.port=7861 - networks: traefik-public: # For local dev, don't expect an external Traefik network diff --git a/deploy/docker-compose.test.yml b/deploy/docker-compose.test.yml new file mode 100644 index 000000000..829aae883 --- /dev/null +++ b/deploy/docker-compose.test.yml @@ -0,0 +1,85 @@ +version: "3.8" +services: + proxy: + ports: + - "80:80" + - "8090:8080" + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker + # Add a constraint to only use services with the label for this stack + # from the env var TRAEFIK_TAG + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `${TRAEFIK_TAG?Variable not set}`) + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Disable Docker Swarm mode for local development + # - --providers.docker.swarmmode + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + # Enable the Dashboard and API in insecure mode for local development + - --api.insecure=true + labels: + - traefik.enable=true + - traefik.http.routers.${STACK_NAME?Variable not set}-traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`) + - traefik.http.services.${STACK_NAME?Variable not set}-traefik-public.loadbalancer.server.port=80 + + result_backend: + ports: + - "6379:6379" + + pgadmin: + ports: + - "5050:5050" + + flower: + ports: + - "5555:5555" + + backend: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=7860 + + frontend: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=PathPrefix(`/`) + - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80 + + celeryworker: + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-celeryworker-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-celeryworker.loadbalancer.server.port=7860 + + tests: + extends: + file: docker-compose.yml + service: backend + env_file: + - .env + build: + context: ../ + dockerfile: base.Dockerfile + command: pytest -vv + healthcheck: + test: "exit 0" + # override deploy labels to avoid conflicts with the backend service + labels: + - traefik.enable=true + - traefik.constraint-label-stack=${TRAEFIK_TAG?Variable not set} + - traefik.http.routers.${STACK_NAME?Variable not set}-tests-http.rule=PathPrefix(`/api/v1`) || PathPrefix(`/docs`) || PathPrefix(`/health`) + - traefik.http.services.${STACK_NAME?Variable not set}-tests.loadbalancer.server.port=7861 + +networks: + traefik-public: + # For local dev, don't expect an external Traefik network + external: false From f497369a1c608e6acae589d76c37b35ec0bfd527 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 18:06:52 -0300 Subject: [PATCH 198/213] =?UTF-8?q?=F0=9F=94=A7=20chore(users.py):=20remov?= =?UTF-8?q?e=20unused=20current=5Fuser=20variable=20in=20read=5Fall=5Fuser?= =?UTF-8?q?s=20function=20=F0=9F=94=A7=20chore(api=5Fkey.py):=20add=20casc?= =?UTF-8?q?ade=20delete=20to=20user-api=5Fkey=20relationship=20to=20ensure?= =?UTF-8?q?=20API=20keys=20are=20deleted=20when=20user=20is=20deleted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 2 +- .../langflow/services/database/models/api_key/api_key.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index 9541bb2d4..aef639b0a 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -67,7 +67,7 @@ def read_current_user( def read_all_users( skip: int = 0, limit: int = 10, - current_user: Session = Depends(get_current_active_superuser), + _: Session = Depends(get_current_active_superuser), session: Session = Depends(get_session), ) -> UsersResponse: """ diff --git a/src/backend/langflow/services/database/models/api_key/api_key.py b/src/backend/langflow/services/database/models/api_key/api_key.py index 5d5bab0f4..0f0d2f889 100644 --- a/src/backend/langflow/services/database/models/api_key/api_key.py +++ b/src/backend/langflow/services/database/models/api_key/api_key.py @@ -22,8 +22,12 @@ class ApiKey(ApiKeyBase, table=True): api_key: str = Field(index=True, unique=True) # User relationship + # Delete API keys when user is deleted user_id: UUID = Field(index=True, foreign_key="user.id") - user: "User" = Relationship(back_populates="api_keys") + user: "User" = Relationship( + back_populates="api_keys", + sa_relationship_kwargs={"cascade": "delete"}, + ) class ApiKeyCreate(ApiKeyBase): From c3df875f1aedbd17040da47d1a905ca853cc3611 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 25 Sep 2023 18:07:06 -0300 Subject: [PATCH 199/213] fix(AdminPage/index.tsx): fix off-by-one error in getUsersPage function call to correctly pass the index parameter The getUsersPage function was being called with the incorrect index parameter. It was off by one, causing the wrong page of users to be fetched. This has been fixed by subtracting 1 from the index parameter before calling the function. --- src/frontend/src/pages/AdminPage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/pages/AdminPage/index.tsx b/src/frontend/src/pages/AdminPage/index.tsx index 24baefb5b..f6e17a32a 100644 --- a/src/frontend/src/pages/AdminPage/index.tsx +++ b/src/frontend/src/pages/AdminPage/index.tsx @@ -63,7 +63,7 @@ export default function AdminPage() { function getUsers() { setLoadingUsers(true); - getUsersPage(index, size) + getUsersPage(index - 1, size) .then((users) => { setTotalRowsCount(users["total_count"]); userList.current = users["users"]; From 3a4ae5e37b4ebce9eca26bcf82eba5c8cb3b570d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 19:12:57 -0300 Subject: [PATCH 200/213] =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20prevent?= =?UTF-8?q?=20non-superusers=20from=20changing=20their=20password?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index aef639b0a..19aaf81d5 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -99,7 +99,7 @@ def patch_user( raise HTTPException( status_code=403, detail="You don't have the permission to update this user" ) - if user_update.password: + if user_update.password and not user.is_superuser: raise HTTPException( status_code=400, detail="You can't change your password here" ) From 4be21db30c779675f047d0b00bef150ac4f0c897 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 19:14:09 -0300 Subject: [PATCH 201/213] =?UTF-8?q?=F0=9F=94=A7=20chore(user.py):=20add=20?= =?UTF-8?q?optional=20'username'=20field=20to=20UserUpdate=20model=20for?= =?UTF-8?q?=20better=20flexibility=20in=20updating=20user=20information?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/database/models/user/user.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/langflow/services/database/models/user/user.py b/src/backend/langflow/services/database/models/user/user.py index 8bd42af00..5d751d080 100644 --- a/src/backend/langflow/services/database/models/user/user.py +++ b/src/backend/langflow/services/database/models/user/user.py @@ -42,6 +42,7 @@ class UserRead(SQLModel): class UserUpdate(SQLModel): + username: Optional[str] = Field() profile_image: Optional[str] = Field() password: Optional[str] = Field() is_active: Optional[bool] = Field() From 1e0f81c1354a5f40f58b620f6971c311494f5f0d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 19:34:17 -0300 Subject: [PATCH 202/213] =?UTF-8?q?=F0=9F=90=9B=20fix(login.py):=20handle?= =?UTF-8?q?=20exceptions=20in=20login=5Fto=5Fget=5Faccess=5Ftoken=20functi?= =?UTF-8?q?on=20to=20provide=20more=20detailed=20error=20messages=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20only=20hash=20password=20if=20u?= =?UTF-8?q?ser=20is=20a=20superuser=20to=20prevent=20regular=20users=20fro?= =?UTF-8?q?m=20changing=20their=20password?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/login.py | 22 +++++++++++++++------- src/backend/langflow/api/v1/users.py | 10 ++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index d45302c2e..e33eb8225 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -23,14 +23,22 @@ async def login_to_get_access_token( db: Session = Depends(get_session), # _: Session = Depends(get_current_active_user) ): - if user := authenticate_user(form_data.username, form_data.password, db): - return create_user_tokens(user_id=user.id, db=db, update_last_login=True) - else: + try: + user = authenticate_user(form_data.username, form_data.password, db) + + if user: + return create_user_tokens(user_id=user.id, db=db, update_last_login=True) + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + except Exception as exc: raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Incorrect username or password", - headers={"WWW-Authenticate": "Bearer"}, - ) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=str(exc), + ) from exc @router.get("/auto_login") diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index 19aaf81d5..73c7346d9 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -99,10 +99,12 @@ def patch_user( raise HTTPException( status_code=403, detail="You don't have the permission to update this user" ) - if user_update.password and not user.is_superuser: - raise HTTPException( - status_code=400, detail="You can't change your password here" - ) + if user_update.password: + if not user.is_superuser: + raise HTTPException( + status_code=400, detail="You can't change your password here" + ) + user_update.password = get_password_hash(user_update.password) if user_db := get_user_by_id(session, user_id): return update_user(user_db, user_update, session) From 62a4e94c71c0dae350e3df24c859b795ad0889a7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 19:58:21 -0300 Subject: [PATCH 203/213] =?UTF-8?q?=F0=9F=94=A7=20fix(api=5Fkey.py):=20rem?= =?UTF-8?q?ove=20unnecessary=20sa=5Frelationship=5Fkwargs=20to=20improve?= =?UTF-8?q?=20code=20readability=20and=20maintainability=20=F0=9F=94=A7=20?= =?UTF-8?q?fix(user.py):=20add=20sa=5Frelationship=5Fkwargs=20to=20api=5Fk?= =?UTF-8?q?eys=20relationship=20to=20enable=20cascading=20delete=20when=20?= =?UTF-8?q?deleting=20a=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/services/database/models/api_key/api_key.py | 1 - src/backend/langflow/services/database/models/user/user.py | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/services/database/models/api_key/api_key.py b/src/backend/langflow/services/database/models/api_key/api_key.py index 0f0d2f889..35aa6c7a9 100644 --- a/src/backend/langflow/services/database/models/api_key/api_key.py +++ b/src/backend/langflow/services/database/models/api_key/api_key.py @@ -26,7 +26,6 @@ class ApiKey(ApiKeyBase, table=True): user_id: UUID = Field(index=True, foreign_key="user.id") user: "User" = Relationship( back_populates="api_keys", - sa_relationship_kwargs={"cascade": "delete"}, ) diff --git a/src/backend/langflow/services/database/models/user/user.py b/src/backend/langflow/services/database/models/user/user.py index 5d751d080..5f2f3e1c4 100644 --- a/src/backend/langflow/services/database/models/user/user.py +++ b/src/backend/langflow/services/database/models/user/user.py @@ -21,7 +21,10 @@ class User(SQLModelSerializable, table=True): create_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) last_login_at: Optional[datetime] = Field() - api_keys: list["ApiKey"] = Relationship(back_populates="user") + api_keys: list["ApiKey"] = Relationship( + back_populates="user", + sa_relationship_kwargs={"cascade": "delete"}, + ) flows: list["Flow"] = Relationship(back_populates="user") From c6882a0598df9805b86293949f93c0ff25e161cd Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 20:56:39 -0300 Subject: [PATCH 204/213] =?UTF-8?q?=F0=9F=90=9B=20fix(login.py):=20move=20?= =?UTF-8?q?user=20authentication=20logic=20outside=20of=20try-except=20blo?= =?UTF-8?q?ck=20to=20ensure=20proper=20error=20handling=20=E2=9C=A8=20feat?= =?UTF-8?q?(login.py):=20add=20endpoint=20for=20auto=20login=20to=20improv?= =?UTF-8?q?e=20user=20experience?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/login.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index e33eb8225..6888d4718 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -25,21 +25,21 @@ async def login_to_get_access_token( ): try: user = authenticate_user(form_data.username, form_data.password, db) - - if user: - return create_user_tokens(user_id=user.id, db=db, update_last_login=True) - else: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Incorrect username or password", - headers={"WWW-Authenticate": "Bearer"}, - ) except Exception as exc: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc), ) from exc + if user: + return create_user_tokens(user_id=user.id, db=db, update_last_login=True) + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + @router.get("/auto_login") async def auto_login( From 12a46b6936e23829d9956d4d5f1fa51faff76137 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 25 Sep 2023 22:09:46 -0300 Subject: [PATCH 205/213] =?UTF-8?q?=F0=9F=90=9B=20fix(login.py):=20re-rais?= =?UTF-8?q?e=20HTTPException=20if=20it=20is=20already=20raised=20to=20prev?= =?UTF-8?q?ent=20overriding=20the=20original=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/login.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/langflow/api/v1/login.py b/src/backend/langflow/api/v1/login.py index 6888d4718..4dfc723b5 100644 --- a/src/backend/langflow/api/v1/login.py +++ b/src/backend/langflow/api/v1/login.py @@ -26,6 +26,8 @@ async def login_to_get_access_token( try: user = authenticate_user(form_data.username, form_data.password, db) except Exception as exc: + if isinstance(exc, HTTPException): + raise exc raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc), From bfdb9de879872e588bd320c9c079371de71ba06e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 10:21:39 -0300 Subject: [PATCH 206/213] =?UTF-8?q?=F0=9F=90=9B=20fix(test=5Fuser.py):=20f?= =?UTF-8?q?ix=20incorrect=20assertion=20in=20test=5Fpatch=5Fuser=20functio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_user.py b/tests/test_user.py index bf2fee012..49962c8d1 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -170,7 +170,7 @@ def test_patch_user(client, active_user, logged_in_headers): response = client.patch( f"/api/v1/users/{user_id}", json=update_data.dict(), headers=logged_in_headers ) - assert response.status_code == 304, response.json() + assert response.status_code == 200, response.json() update_data = UserUpdate( profile_image="new_image", ) From afa66112e1aea4b2bbae7a687817e65eb2d7e3c4 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 10:21:59 -0300 Subject: [PATCH 207/213] =?UTF-8?q?=F0=9F=93=9D=20docs(login.mdx):=20add?= =?UTF-8?q?=20documentation=20for=20login=20functionality=20in=20Langflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The login functionality in Langflow serves to authenticate users and protect sensitive routes in the application. This commit adds documentation for the login functionality, including information about the enhanced login mechanism introduced in version 0.5. It explains the environment variables that are crucial in configuring the login settings and provides instructions on how to set them securely. It also covers the command-line interface for managing superusers and provides details on the sign-up process and profile settings for users. Screenshots are included to illustrate the sign-up page, profile settings page, and admin page. --- docs/docs/{guides => guidelines}/login.mdx | 6 ++++++ 1 file changed, 6 insertions(+) rename docs/docs/{guides => guidelines}/login.mdx (96%) diff --git a/docs/docs/guides/login.mdx b/docs/docs/guidelines/login.mdx similarity index 96% rename from docs/docs/guides/login.mdx rename to docs/docs/guidelines/login.mdx index a2d7c5502..85ca1371f 100644 --- a/docs/docs/guides/login.mdx +++ b/docs/docs/guidelines/login.mdx @@ -20,6 +20,12 @@ The following environment variables are crucial in configuring the login setting - _`LANGFLOW_SECRET_KEY`_: A key used for encrypting the superuser's password. - _`LANGFLOW_NEW_USER_IS_ACTIVE`_: Determines whether new users are automatically activated. Default is `False`. +All of these variables can be passed to the CLI command _`langflow run`_ through the _`--env-file`_ option. For example: + +```bash +langflow run --env-file .env +``` + It is critical not to expose these environment variables in your code repository. Always set them securely in your deployment environment, for From 3bb755bbebf54e5d9ffb965dd94c93b3edd15192 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 10:33:34 -0300 Subject: [PATCH 208/213] =?UTF-8?q?=F0=9F=93=9A=20docs(sidebars.js):=20add?= =?UTF-8?q?=20missing=20login=20and=20api=20guidelines=20and=20remove=20lo?= =?UTF-8?q?gin=20guide=20from=20step-by-step=20guides=20=F0=9F=93=9A=20doc?= =?UTF-8?q?s(sidebars.js):=20add=20missing=20login=20and=20api=20guideline?= =?UTF-8?q?s=20to=20the=20guidelines=20section=20and=20remove=20login=20gu?= =?UTF-8?q?ide=20from=20step-by-step=20guides?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/sidebars.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sidebars.js b/docs/sidebars.js index 4624190ba..116e54616 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -16,6 +16,8 @@ module.exports = { label: "Guidelines", collapsed: false, items: [ + "guidelines/login", + "guidelines/api", "guidelines/components", "guidelines/features", "guidelines/collection", @@ -52,7 +54,6 @@ module.exports = { label: "Step-by-Step Guides", collapsed: false, items: [ - "guides/login", "guides/loading_document", "guides/chatprompttemplate_guide", "guides/langfuse_integration", From a85b39e04891f09e99a087f6a6bc7f35ff905022 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 10:33:53 -0300 Subject: [PATCH 209/213] =?UTF-8?q?=F0=9F=94=A5=20refactor(sidebars.js):?= =?UTF-8?q?=20comment=20out=20"guidelines/api"=20section=20to=20temporaril?= =?UTF-8?q?y=20remove=20it=20from=20the=20sidebar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ℹ️ The "guidelines/api" section is temporarily removed from the sidebar to prevent confusion and ensure consistency while it is being updated or revised. --- docs/sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sidebars.js b/docs/sidebars.js index 116e54616..166d72f22 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -17,7 +17,7 @@ module.exports = { collapsed: false, items: [ "guidelines/login", - "guidelines/api", + // "guidelines/api", "guidelines/components", "guidelines/features", "guidelines/collection", From 41d18edc97a0272e52e4d2efbfd693cc3578ce1e Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 26 Sep 2023 10:46:55 -0300 Subject: [PATCH 210/213] update package lock json --- src/frontend/package-lock.json | 8773 ++++++++++++++++++++++++++++++-- 1 file changed, 8281 insertions(+), 492 deletions(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index c4a0660cb..2a02d8c14 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -1,7 +1,7 @@ { "name": "langflow", "version": "0.1.2", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "": { @@ -219,29 +219,29 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz", - "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.22.17", - "@babel/helpers": "^7.22.15", - "@babel/parser": "^7.22.16", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.17", - "@babel/types": "^7.22.17", - "convert-source-map": "^1.7.0", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -255,6 +255,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -264,11 +269,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -314,20 +319,20 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -356,15 +361,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", - "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.15" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -404,9 +409,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", - "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -420,24 +425,24 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", - "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -510,9 +515,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -521,9 +526,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", - "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -545,18 +550,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", - "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.17", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -565,12 +570,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.17", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", - "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.15", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -710,6 +715,51 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/darwin-arm64": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", @@ -725,21 +775,291 @@ "node": ">=12" } }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@floating-ui/core": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", - "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", "dependencies": { - "@floating-ui/utils": "^0.1.1" + "@floating-ui/utils": "^0.1.3" } }, "node_modules/@floating-ui/dom": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.2.tgz", - "integrity": "sha512-6ArmenS6qJEWmwzczWyhvrXRdI/rI78poBcW0h/456+onlabit+2G+QxHx5xTOX60NBJQXjsCLFbW2CmsXpUog==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", "dependencies": { - "@floating-ui/core": "^1.4.1", - "@floating-ui/utils": "^0.1.1" + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" } }, "node_modules/@floating-ui/react-dom": { @@ -755,9 +1075,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.2.tgz", - "integrity": "sha512-ou3elfqG/hZsbmF4bxeJhPHIf3G2pm0ujc39hYEZrfVqt7Vk/Zji6CXc3W0pmYM8BW1g40U+akTl9DKZhFhInQ==" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz", + "integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==" }, "node_modules/@headlessui/react": { "version": "1.7.17", @@ -845,14 +1165,14 @@ } }, "node_modules/@mui/base": { - "version": "5.0.0-beta.15", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.15.tgz", - "integrity": "sha512-Xtom3YSdi0iwYPtyVRFUEGoRwi6IHWixPwifDKaK+4PkEPtUWMU5YOIJfTsmC59ri+dFvA3oBNSiTPUGGrklZw==", + "version": "5.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.17.tgz", + "integrity": "sha512-xNbk7iOXrglNdIxFBN0k3ySsPIFLWCnFxqsAYl7CIcDkD9low4kJ7IUuy6ctwx/HAy2fenrT3KXHr1sGjAMgpQ==", "dependencies": { "@babel/runtime": "^7.22.15", "@floating-ui/react-dom": "^2.0.2", "@mui/types": "^7.2.4", - "@mui/utils": "^5.14.9", + "@mui/utils": "^5.14.11", "@popperjs/core": "^2.11.8", "clsx": "^2.0.0", "prop-types": "^15.8.1" @@ -884,25 +1204,25 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.9.tgz", - "integrity": "sha512-JAU/R5hM3l2zP1Q4KnioDRhq5V3vZ4mmjEZ+TwARDb2xFhg3p59McacQuzkSu0sUHJnH9aJos36+hU5sPQBcFQ==", + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.11.tgz", + "integrity": "sha512-uY8FLQURhXe3f3O4dS5OSGML9KDm9+IE226cBu78jarVIzdQGPlXwGIlSI9VJR8MvZDA6C0+6XfWDhWCHruC5Q==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" } }, "node_modules/@mui/material": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.9.tgz", - "integrity": "sha512-pbBy5kc5iUGXPxgbb+t+yEPvLK5nE3bPUb8WbAafJ8iZ40ZGui0xC4xiiIyzbVexzsLmyN7MaSo4LkxLmPKqUQ==", + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.11.tgz", + "integrity": "sha512-DnSdJzcR7lwG12JA5L2t8JF+RDzMygu5rCNW+logWb/KW2/TRzwLyVWO+CorHTBjBRd38DBxnwOCDiYkDd+N3A==", "dependencies": { "@babel/runtime": "^7.22.15", - "@mui/base": "5.0.0-beta.15", - "@mui/core-downloads-tracker": "^5.14.9", - "@mui/system": "^5.14.9", + "@mui/base": "5.0.0-beta.17", + "@mui/core-downloads-tracker": "^5.14.11", + "@mui/system": "^5.14.11", "@mui/types": "^7.2.4", - "@mui/utils": "^5.14.9", + "@mui/utils": "^5.14.11", "@types/react-transition-group": "^4.4.6", "clsx": "^2.0.0", "csstype": "^3.1.2", @@ -945,12 +1265,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.9.tgz", - "integrity": "sha512-0PzoUFqFXTXiNchhR7K4b7kZunasPOjx6Qf7AagCmfZDNASHedA0x6evHVhnST918x/AHY9xykYNKfB0Z4xMBg==", + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.11.tgz", + "integrity": "sha512-MSnNNzTu9pfKLCKs1ZAKwOTgE4bz+fQA0fNr8Jm7NDmuWmw0CaN9Vq2/MHsatE7+S0A25IAKby46Uv1u53rKVQ==", "dependencies": { "@babel/runtime": "^7.22.15", - "@mui/utils": "^5.14.9", + "@mui/utils": "^5.14.11", "prop-types": "^15.8.1" }, "engines": { @@ -971,15 +1291,14 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.9.tgz", - "integrity": "sha512-LEQxLrW9oWvea33pge08+oyNeTz704jb6Nhe26xEJKojXWd34Rr327Zzx3dmo70AcS4h0b99vQjEpUzm6ASqUw==", + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.11.tgz", + "integrity": "sha512-jdUlqRgTYQ8RMtPX4MbRZqar6W2OiIb6J5KEFbIu4FqvPrk44Each4ppg/LAqp1qNlBYq5i+7Q10MYLMpDxX9A==", "dependencies": { "@babel/runtime": "^7.22.15", "@emotion/cache": "^11.11.0", "csstype": "^3.1.2", - "prop-types": "^15.8.1", - "react": "^18.2.0" + "prop-types": "^15.8.1" }, "engines": { "node": ">=12.0.0" @@ -1003,15 +1322,15 @@ } }, "node_modules/@mui/system": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.9.tgz", - "integrity": "sha512-Z00Wj590QXk5+SIxmxayBo7SWrao+y433LKGChneJxO4QcT/caSCeEWtyeoLs1Q8ys0zOzl2kkKee6n8TaKzhQ==", + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.11.tgz", + "integrity": "sha512-yl8xV+y0k7j6dzBsHabKwoShmjqLa8kTxrhUI3JpqLG358VRVMJRW/ES0HhvfcCi4IVXde+Tc2P3K1akGL8zoA==", "dependencies": { "@babel/runtime": "^7.22.15", - "@mui/private-theming": "^5.14.9", - "@mui/styled-engine": "^5.14.9", + "@mui/private-theming": "^5.14.11", + "@mui/styled-engine": "^5.14.11", "@mui/types": "^7.2.4", - "@mui/utils": "^5.14.9", + "@mui/utils": "^5.14.11", "clsx": "^2.0.0", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -1063,11 +1382,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.9.tgz", - "integrity": "sha512-9ysB5e+RwS7ofn0n3nwAg1/3c81vBTmSvauD3EuK9LmqMzhmF//BFDaC44U4yITvB/0m1kWyDqg924Ll3VHCcg==", + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.11.tgz", + "integrity": "sha512-fmkIiCPKyDssYrJ5qk+dime1nlO3dmWfCtaPY/uVBqCRMBZ11JhddB9m8sjI2mgqQQwRJG5bq3biaosNdU/s4Q==", "dependencies": { "@babel/runtime": "^7.22.15", + "@types/prop-types": "^15.7.5", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -1080,8 +1400,7 @@ }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1122,12 +1441,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz", - "integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", + "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", "dev": true, "dependencies": { - "playwright": "1.38.0" + "playwright": "1.38.1" }, "bin": { "playwright": "cli.js" @@ -1336,19 +1655,19 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.4.tgz", - "integrity": "sha512-hJtRy/jPULGQZceSAP2Re6/4NpKo8im6V8P2hUqZsdFiSL8l35kYsw3qbRI6Ay5mQd2+wlLqje770eq+RJ3yZg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-focus-scope": "1.0.4", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2", @@ -1389,9 +1708,9 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", - "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", @@ -1416,16 +1735,16 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.5.tgz", - "integrity": "sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", + "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-menu": "2.0.5", + "@radix-ui/react-menu": "2.0.6", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1" }, @@ -1462,9 +1781,9 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", - "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", @@ -1564,9 +1883,9 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.5.tgz", - "integrity": "sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", @@ -1574,12 +1893,12 @@ "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-focus-scope": "1.0.4", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-roving-focus": "1.0.4", @@ -1604,9 +1923,9 @@ } }, "node_modules/@radix-ui/react-menubar": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.0.3.tgz", - "integrity": "sha512-GqjdxzYCjjKhcgEODDP8SrYfbWNh/Hm3lyuFkP5Q5IbX0QfXklLF1o1AqA3oTV2kulUgN/kOZVS92hIIShEgpA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.0.4.tgz", + "integrity": "sha512-bHgUo9gayKZfaQcWSSLr++LyS0rgh+MvD89DE4fJ6TkGHvjHgPaBZf44hdka7ogOxIOdj9163J+5xL2Dn4qzzg==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", @@ -1615,7 +1934,7 @@ "@radix-ui/react-context": "1.0.1", "@radix-ui/react-direction": "1.0.1", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-menu": "2.0.5", + "@radix-ui/react-menu": "2.0.6", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-roving-focus": "1.0.4", "@radix-ui/react-use-controllable-state": "1.0.1" @@ -1636,20 +1955,20 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.6.tgz", - "integrity": "sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", + "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-focus-scope": "1.0.4", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2", @@ -1673,9 +1992,9 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", - "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", "dependencies": { "@babel/runtime": "^7.13.10", "@floating-ui/react-dom": "^2.0.0", @@ -1705,9 +2024,9 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", - "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" @@ -1872,6 +2191,113 @@ } } }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", + "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-separator": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz", @@ -1973,18 +2399,18 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz", - "integrity": "sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", + "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2", @@ -2161,11 +2587,11 @@ } }, "node_modules/@reactflow/background": { - "version": "11.2.8", - "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.2.8.tgz", - "integrity": "sha512-5o41N2LygiNC2/Pk8Ak2rIJjXbKHfQ23/Y9LFsnAlufqwdzFqKA8txExpsMoPVHHlbAdA/xpQaMuoChGPqmyDw==", + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.1.tgz", + "integrity": "sha512-FjYHXvzwwaCJJS5rRxXbU4r8xbAqEvLQkO1l4czjWgrE0VQELC93RKVI4lRpkv2RkkIwRNMvoGb6kXNbJWiU/g==", "dependencies": { - "@reactflow/core": "11.8.3", + "@reactflow/core": "11.9.1", "classcat": "^5.0.3", "zustand": "^4.4.1" }, @@ -2175,11 +2601,11 @@ } }, "node_modules/@reactflow/controls": { - "version": "11.1.19", - "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.1.19.tgz", - "integrity": "sha512-Vo0LFfAYjiSRMLEII/aeBo+1MT2a0Yc7iLVnkuRTLzChC0EX+A2Fa+JlzeOEYKxXlN4qcDxckRNGR7092v1HOQ==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.1.tgz", + "integrity": "sha512-mW8kz/J77tc5uRwSTSUH1OtkksaVc52Dbz7aTuF0aKZXwPHoAfRPFeet2VLQV1IdKeSab82Mqy0WTSJj6Hl15w==", "dependencies": { - "@reactflow/core": "11.8.3", + "@reactflow/core": "11.9.1", "classcat": "^5.0.3", "zustand": "^4.4.1" }, @@ -2189,9 +2615,9 @@ } }, "node_modules/@reactflow/core": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.8.3.tgz", - "integrity": "sha512-y6DN8Wy4V4KQBGHFqlj9zWRjLJU6CgdnVwWaEA/PdDg/YUkFBMpZnXqTs60czinoA2rAcvsz50syLTPsj5e+Wg==", + "version": "11.9.1", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.9.1.tgz", + "integrity": "sha512-qXWwB2KSWkp3MH3fhY1HL4yFBvMZ+7y9mkfk0we7wCuGNgQzWVJDwvVEpPTkyp5xyxX53sSF3UDC+UaFCB4ElA==", "dependencies": { "@types/d3": "^7.4.0", "@types/d3-drag": "^3.0.1", @@ -2209,11 +2635,11 @@ } }, "node_modules/@reactflow/minimap": { - "version": "11.6.3", - "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.6.3.tgz", - "integrity": "sha512-PSA28dk09RnBHOA1zb45fjQXz3UozSJZmsIpgq49O3trfVFlSgRapxNdGsughWLs7/emg2M5jmi6Vc+ejcfjvQ==", + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.1.tgz", + "integrity": "sha512-ZNHx1wURS055kYYaiFhrmGYI2UXA7Cwcjxfww4Mq3PWvIIaEFL9t34aUWoXCYm+NPFHElwC8jM7Vyp7xDR9oyg==", "dependencies": { - "@reactflow/core": "11.8.3", + "@reactflow/core": "11.9.1", "@types/d3-selection": "^3.0.3", "@types/d3-zoom": "^3.0.1", "classcat": "^5.0.3", @@ -2227,11 +2653,11 @@ } }, "node_modules/@reactflow/node-resizer": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.1.5.tgz", - "integrity": "sha512-z/hJlsptd2vTx13wKouqvN/Kln08qbkA+YTJLohc2aJ6rx3oGn9yX4E4IqNxhA7zNqYEdrnc1JTEA//ifh9z3w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.1.tgz", + "integrity": "sha512-nUh73WHVAYsJLvgSKqKAxIPU9vUkEM5iold2zQsEOHqjfnk86m1r33fTEiw5+MPqCFHMoGzyZe2gxXUMFqg/3A==", "dependencies": { - "@reactflow/core": "11.8.3", + "@reactflow/core": "11.9.1", "classcat": "^5.0.4", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", @@ -2243,11 +2669,11 @@ } }, "node_modules/@reactflow/node-toolbar": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.2.7.tgz", - "integrity": "sha512-vs+Wg1tjy3SuD7eoeTqEtscBfE9RY+APqC28urVvftkrtsN7KlnoQjqDG6aE45jWP4z+8bvFizRWjAhxysNLkg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.1.tgz", + "integrity": "sha512-YkrpB2+/9ymZ2h1nmuKytmrFlIXdiDzeDqmZnyJF2UNH/4sSo/GswPJmqiA8ETOP2oEdIkNmO9dwVpiDvWtZ5A==", "dependencies": { - "@reactflow/core": "11.8.3", + "@reactflow/core": "11.9.1", "classcat": "^5.0.3", "zustand": "^4.4.1" }, @@ -2298,9 +2724,9 @@ } }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", "engines": { "node": ">=14" }, @@ -2313,9 +2739,9 @@ } }, "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", "engines": { "node": ">=14" }, @@ -2328,9 +2754,9 @@ } }, "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz", - "integrity": "sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", "engines": { "node": ">=14" }, @@ -2343,9 +2769,9 @@ } }, "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-7.0.0.tgz", - "integrity": "sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", "engines": { "node": ">=14" }, @@ -2358,9 +2784,9 @@ } }, "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-7.0.0.tgz", - "integrity": "sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", "engines": { "node": ">=14" }, @@ -2373,9 +2799,9 @@ } }, "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-7.0.0.tgz", - "integrity": "sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", "engines": { "node": ">=14" }, @@ -2388,9 +2814,9 @@ } }, "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-7.0.0.tgz", - "integrity": "sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", "engines": { "node": ">=14" }, @@ -2403,9 +2829,9 @@ } }, "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-7.0.0.tgz", - "integrity": "sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", "engines": { "node": ">=12" }, @@ -2418,18 +2844,18 @@ } }, "node_modules/@svgr/babel-preset": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-7.0.0.tgz", - "integrity": "sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^7.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^7.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^7.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^7.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "^7.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "^7.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "^7.0.0", - "@svgr/babel-plugin-transform-svg-component": "^7.0.0" + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" }, "engines": { "node": ">=14" @@ -2443,14 +2869,15 @@ } }, "node_modules/@svgr/core": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-7.0.0.tgz", - "integrity": "sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dependencies": { "@babel/core": "^7.21.3", - "@svgr/babel-preset": "^7.0.0", + "@svgr/babel-preset": "8.1.0", "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3" + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" }, "engines": { "node": ">=14" @@ -2486,9 +2913,9 @@ } }, "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-7.0.0.tgz", - "integrity": "sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" @@ -2513,13 +2940,13 @@ } }, "node_modules/@svgr/plugin-jsx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-7.0.0.tgz", - "integrity": "sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dependencies": { "@babel/core": "^7.21.3", - "@svgr/babel-preset": "^7.0.0", - "@svgr/hast-util-to-babel-ast": "^7.0.0", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" }, "engines": { @@ -2528,6 +2955,9 @@ "funding": { "type": "github", "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" } }, "node_modules/@swc/cli": { @@ -2571,13 +3001,14 @@ } }, "node_modules/@swc/core": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.84.tgz", - "integrity": "sha512-UPKUiDwG7HOdPfOb1VFeEJ76JDgU2w80JLewzx6tb0fk9TIjhr9yxKBzPbzc/QpjGHDu5iaEuNeZcu27u4j63g==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.89.tgz", + "integrity": "sha512-+FchWateF57g50ChX6++QQDwgVd6iWZX5HA6m9LRIdJIB56bIqbwRQDwVL3Q8Rlbry4kmw+RxiOW2FjAx9mQOQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@swc/types": "^0.1.4" + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" }, "engines": { "node": ">=10" @@ -2587,16 +3018,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.84", - "@swc/core-darwin-x64": "1.3.84", - "@swc/core-linux-arm-gnueabihf": "1.3.84", - "@swc/core-linux-arm64-gnu": "1.3.84", - "@swc/core-linux-arm64-musl": "1.3.84", - "@swc/core-linux-x64-gnu": "1.3.84", - "@swc/core-linux-x64-musl": "1.3.84", - "@swc/core-win32-arm64-msvc": "1.3.84", - "@swc/core-win32-ia32-msvc": "1.3.84", - "@swc/core-win32-x64-msvc": "1.3.84" + "@swc/core-darwin-arm64": "1.3.89", + "@swc/core-darwin-x64": "1.3.89", + "@swc/core-linux-arm-gnueabihf": "1.3.89", + "@swc/core-linux-arm64-gnu": "1.3.89", + "@swc/core-linux-arm64-musl": "1.3.89", + "@swc/core-linux-x64-gnu": "1.3.89", + "@swc/core-linux-x64-musl": "1.3.89", + "@swc/core-win32-arm64-msvc": "1.3.89", + "@swc/core-win32-ia32-msvc": "1.3.89", + "@swc/core-win32-x64-msvc": "1.3.89" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -2608,9 +3039,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.84.tgz", - "integrity": "sha512-mqK0buOo+toF2HoJ/gWj2ApZbvbIiNq3mMwSTHCYJHlQFQfoTWnl9aaD5GSO4wfNFVYfEZ1R259o5uv5NlVtoA==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.89.tgz", + "integrity": "sha512-LVCZQ2yGrX2678uMvW66IF1bzcOMqiABi+ioNDnJtAIsE/zRVMEYp1ivbOrH32FmPplBby6CGgJIOT3P4VaP1g==", "cpu": [ "arm64" ], @@ -2624,9 +3055,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.84.tgz", - "integrity": "sha512-cyuQZz62C43EDZqtnptUTlfDvAjgG3qu139m5zsfIK6ltXA5inKFbDWV3a/M5c18dFzA2Xh21Q46XZezmtQ9Tg==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.89.tgz", + "integrity": "sha512-IwKlX65YrPBF3urOxBJia0PjnZeaICnCkSwGLiYyV1RhM8XwZ/XyEDTBEsdph3WxUM5wCZQSk8UY/d0saIsX9w==", "cpu": [ "x64" ], @@ -2640,9 +3071,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.84.tgz", - "integrity": "sha512-dmt/ECQrp3ZPWnK27p4E4xRIRHOoJhgGvxC5t5YaWzN20KcxE9ykEY2oLGSoeceM/A+4D11aRYGwF/EM7yOkvA==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.89.tgz", + "integrity": "sha512-u5qAPh7NkKoDJYwfaB5zuRvzW2+A89CQQHp5xcYjpctRsk3sUrPmC7vNeE12xipBNKLujIG59ppbrf6Pkp5XIg==", "cpu": [ "arm" ], @@ -2656,9 +3087,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.84.tgz", - "integrity": "sha512-PgVfrI3NVg2z/oeg3GWLb9rFLMqidbdPwVH5nRyHVP2RX/BWP6qfnYfG+gJv4qrKzIldb9TyCGH7y8VWctKLxw==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.89.tgz", + "integrity": "sha512-eykuO7XtPltk600HvnnRr1nU5qGk7PeqLmztHA7R2bu2SbtcbCGsewPNcAX5eP8by2VwpGcLPdxaKyqeUwCgoA==", "cpu": [ "arm64" ], @@ -2672,9 +3103,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.84.tgz", - "integrity": "sha512-hcuEa8/vin4Ns0P+FpcDHQ4f3jmhgGKQhqw0w+TovPSVTIXr+nrFQ2AGhs9nAxS6tSQ77C53Eb5YRpK8ToFo1A==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.89.tgz", + "integrity": "sha512-i/65Vt3ljfd6EyR+WWZ5aAjZLTQMIHoR+Ay97jE0kysRn8MEOINu0SWyiEwcdXzRGlt+zkrKYfOxp745sWPDAw==", "cpu": [ "arm64" ], @@ -2688,9 +3119,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.84.tgz", - "integrity": "sha512-IvyimSbwGdu21jBBEqR1Up8Jhvl8kIAf1k3e5Oy8oRfgojdUfmW1EIwgGdoUeyQ1VHlfquiWaRGfsnHQUKl35g==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.89.tgz", + "integrity": "sha512-ERETXe68CJRdNkL3EIN62gErh3p6+/6hmz4C0epnYJ4F7QspdW/EOluL1o9bl4dux4Xz0nmBPSZsqfHq/nl1KA==", "cpu": [ "x64" ], @@ -2704,9 +3135,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.84.tgz", - "integrity": "sha512-hdgVU/O5ufDCe+p5RtCjU7PRNwd0WM+eWJS+GNY4QWL6O8y2VLM+i4+6YzwSUjeBk0xd+1YElMxbqz7r5tSZhw==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.89.tgz", + "integrity": "sha512-EXiwgU5E/yC5zuJtOXXWv+wMwpe5DR380XhVxIOBG6nFi6MR3O2X37KxeEdQZX8RwN7/KU6kNHeifzEiSvixfA==", "cpu": [ "x64" ], @@ -2720,9 +3151,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.84.tgz", - "integrity": "sha512-rzH6k2BF0BFOFhUTD+bh0oCiUCZjFfDfoZoYNN/CM0qbtjAcFH21hzMh/EH8ZaXq8k/iQmUNNa5MPNPZ4SOMNw==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.89.tgz", + "integrity": "sha512-j7GvkgeOrZlB55MpEwX+6E6KjxwOmwRXpIqMjF11JDIZ0wEwHlBxZhlnQQ58iuI6jL6AJgDH/ktDhMyELoBiHw==", "cpu": [ "arm64" ], @@ -2736,9 +3167,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.84.tgz", - "integrity": "sha512-Y+Dk7VLLVwwsAzoDmjkNW/sTmSPl9PGr4Mj1nhc5A2NNxZ+hz4SxFMclacDI03SC5ikK8Qh6WOoE/+nwUDa3uA==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.89.tgz", + "integrity": "sha512-n57nE7d3FXBa3Y2+VoJdPulcUAS0ZGAGVGxFpeM/tZt1MBEN5OvpOSOIp35dK5HAAxAzTPlmqj9KUYnVxLMVKw==", "cpu": [ "ia32" ], @@ -2752,9 +3183,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.84.tgz", - "integrity": "sha512-WmpaosqCWMX7DArLdU8AJcj96hy0PKlYh1DaMVikSrrDHbJm2dZ8rd27IK3qUB8DgPkrDYHmLAKNZ+z3gWXgRQ==", + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.89.tgz", + "integrity": "sha512-6yMAmqgseAwEXFIwurP7CL8yIH8n7/Rg62ooOVSLSWL5O/Pwlpy1WrpoA0eKhgMLLkIrPvNuKaE/rG7c2iNQHA==", "cpu": [ "x64" ], @@ -2767,10 +3198,16 @@ "node": ">=10" } }, + "node_modules/@swc/counter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.1.tgz", + "integrity": "sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==", + "dev": true + }, "node_modules/@swc/types": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.4.tgz", - "integrity": "sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", "dev": true }, "node_modules/@szmarczak/http-timer": { @@ -2786,20 +3223,20 @@ } }, "node_modules/@tabler/icons": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.34.0.tgz", - "integrity": "sha512-65GsJsT4ZBETWcdrNxbsjsbRoZvbVk3CcU2SafaElrzP1wpOeuAn9aELVEbxhdyZyP9dg2SCfgH6iAArJgp7lw==", + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.35.0.tgz", + "integrity": "sha512-qW/itKdmFvfGw6mAQ+cZy+2MYTXb0XdGAVhO3obYLJEfsSPMwQRO0S9ckFk1xMQX/Tj7REC3TEmWUBWNi3/o3g==", "funding": { "type": "github", "url": "https://github.com/sponsors/codecalm" } }, "node_modules/@tabler/icons-react": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.34.0.tgz", - "integrity": "sha512-HNNTdwwAW8IwUxLPY2HWFw+d7nPVByieamIx90qM1+Rq8UfvLjwsBFNcUKaJcfRFIAa80o4g6ZGy/NMD/Zw73A==", + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.35.0.tgz", + "integrity": "sha512-jK2zgtMF2+LSjtbjYsfer+ryz72TwyJqv3MsmCfEpQwP37u01xvmTlVhJ+ox3bV+trsgjojsldVDuB05JuXLaw==", "dependencies": { - "@tabler/icons": "2.34.0", + "@tabler/icons": "2.35.0", "prop-types": "^15.7.2" }, "funding": { @@ -2845,9 +3282,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", - "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", + "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", "dev": true, "peer": true, "dependencies": { @@ -3000,9 +3437,9 @@ } }, "node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.2.tgz", + "integrity": "sha512-PHKZuMN+K5qgKIWhBodXzQslTo5P+K/6LqeKXS6O/4liIDdZqaX5RXrCK++LAw+y/nptN48YmUMFiQHRSWYwtQ==", "dev": true }, "node_modules/@types/axios": { @@ -3032,9 +3469,9 @@ "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" }, "node_modules/@types/d3": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", - "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.1.tgz", + "integrity": "sha512-lBpYmbHTCtFKO1DB1R7E9dXp9/g1F3JXSGOF7iKPZ+wRmYg/Q6tCRHODGOc5Qk25fJRe2PI60EDRf2HLPUncMA==", "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", @@ -3069,67 +3506,67 @@ } }, "node_modules/@types/d3-array": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.7.tgz", - "integrity": "sha512-4/Q0FckQ8TBjsB0VdGFemJOG8BLXUB2KKlL0VmZ+eOYeOnTb/wDRQqYWpBmQ6IlvWkXwkYiot+n9Px2aTJ7zGQ==" + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.8.tgz", + "integrity": "sha512-2xAVyAUgaXHX9fubjcCbGAUOqYfRJN1em1EKR2HfzWBpObZhwfnZKvofTN4TplMqJdFQao61I+NVSai/vnBvDQ==" }, "node_modules/@types/d3-axis": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.3.tgz", - "integrity": "sha512-SE3x/pLO/+GIHH17mvs1uUVPkZ3bHquGzvZpPAh4yadRy71J93MJBpgK/xY8l9gT28yTN1g9v3HfGSFeBMmwZw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.4.tgz", + "integrity": "sha512-ySnjI/7qm+J602VjcejXcqs1hEuu5UBbGaJGp+Cn/yKVc1iS3JueLVpToGdQsS2sqta7tqA/kG4ore/+LH90UA==", "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-brush": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.3.tgz", - "integrity": "sha512-MQ1/M/B5ifTScHSe5koNkhxn2mhUPqXjGuKjjVYckplAPjP9t2I2sZafb/YVHDwhoXWZoSav+Q726eIbN3qprA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.4.tgz", + "integrity": "sha512-Kg5uIsdJNMCs5lTqeZFsTKqj9lBvpiFRDkYN3j2CDlPhonNDg9/gXVpv1E/MKh3tEqArryIj9o6RBGE/MQe+6Q==", "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-chord": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.3.tgz", - "integrity": "sha512-keuSRwO02c7PBV3JMWuctIfdeJrVFI7RpzouehvBWL4/GGUB3PBNg/9ZKPZAgJphzmS2v2+7vr7BGDQw1CAulw==" + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.4.tgz", + "integrity": "sha512-p4PvN1N+7GL3Y/NI9Ug1TKwowUV6h664kmxL79ctp1HRYCk1mhP0+SXhjRsoWXCdnJfbLLLmpV99rt8dMrHrzg==" }, "node_modules/@types/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.1.tgz", + "integrity": "sha512-CSAVrHAtM9wfuLJ2tpvvwCU/F22sm7rMHNN+yh9D6O6hyAms3+O0cgMpC1pm6UEUMOntuZC8bMt74PteiDUdCg==" }, "node_modules/@types/d3-contour": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.3.tgz", - "integrity": "sha512-x7G/tdDZt4m09XZnG2SutbIuQqmkNYqR9uhDMdPlpJbcwepkEjEWG29euFcgVA1k6cn92CHdDL9Z+fOnxnbVQw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.4.tgz", + "integrity": "sha512-B0aeX8Xg3MNUglULxqDvlgY1SVXuN2xtEleYSAY0iMhl/SMVT7snzgAveejjwM3KaWuNXIoXEJ7dmXE8oPq/jA==", "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "node_modules/@types/d3-delaunay": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", - "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-WplUJ/OHU7eITneDqNnzK+2pgR+WDzUHG6XAUVo+oWHPQq74VcgUdw8a4ODweaZzF56OVYK+x9GxCyuq6hSu1A==" }, "node_modules/@types/d3-dispatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.3.tgz", - "integrity": "sha512-Df7KW3Re7G6cIpIhQtqHin8yUxUHYAqiE41ffopbmU5+FifYUNV7RVyTg8rQdkEagg83m14QtS8InvNb95Zqug==" + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.4.tgz", + "integrity": "sha512-NApHpGHRNxUy7e2Lfzl/cwOucmn4Xdx6FdmXzAoomo8T81LyGmlBjjko/vP0TVzawlvEFLDq8OCRLulW6DDzKw==" }, "node_modules/@types/d3-drag": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.3.tgz", - "integrity": "sha512-82AuQMpBQjuXeIX4tjCYfWjpm3g7aGCfx6dFlxX2JlRaiME/QWcHzBsINl7gbHCODA2anPYlL31/Trj/UnjK9A==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.4.tgz", + "integrity": "sha512-/t53K1erTuUbP7WIX9SE0hlmytpTYRbIthlhbGkBHzCV5vPO++7yrk8OlisWPyIJO5TGowTmqCtGH2tokY5T/g==", "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-dsv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.2.tgz", - "integrity": "sha512-DooW5AOkj4AGmseVvbwHvwM/Ltu0Ks0WrhG6r5FG9riHT5oUUTHz6xHsHqJSVU8ZmPkOqlUEY2obS5C9oCIi2g==" + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.4.tgz", + "integrity": "sha512-YxfUVJ55HxR8oq88136w09mBMPNhgH7PZjteq72onWXWOohGif/cLQnQv8V4A5lEGjXF04LhwSTpmzpY9wyVyA==" }, "node_modules/@types/d3-ease": { "version": "3.0.0", @@ -3137,40 +3574,40 @@ "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" }, "node_modules/@types/d3-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.3.tgz", - "integrity": "sha512-/EsDKRiQkby3Z/8/AiZq8bsuLDo/tYHnNIZkUpSeEHWV7fHUl6QFBjvMPbhkKGk9jZutzfOkGygCV7eR/MkcXA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.4.tgz", + "integrity": "sha512-RleYajubALkGjrvatxWhlygfvB1KNF0Uzz9guRUeeA+M/2B7l8rxObYdktaX9zU1st04lMCHjZWe4vbl+msH2Q==", "dependencies": { "@types/d3-dsv": "*" } }, "node_modules/@types/d3-force": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.5.tgz", - "integrity": "sha512-EGG+IWx93ESSXBwfh/5uPuR9Hp8M6o6qEGU7bBQslxCvrdUBQZha/EFpu/VMdLU4B0y4Oe4h175nSm7p9uqFug==" + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.6.tgz", + "integrity": "sha512-G9wbOvCxkNlLrppoHLZ6oFpbm3z7ibfkXwLD8g5/4Aa7iTEV0Z7TQ0OL8UxAtvdOhCa2VZcSuqn1NQqyCEqmiw==" }, "node_modules/@types/d3-format": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", - "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.2.tgz", + "integrity": "sha512-9oQWvKk2qVBo49FQq8yD/et8Lx0W5Ac2FdGSOUecqOFKqh0wkpyHqf9Qc7A06ftTR+Lz13Pi3jHIQis0aCueOA==" }, "node_modules/@types/d3-geo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.4.tgz", - "integrity": "sha512-kmUK8rVVIBPKJ1/v36bk2aSgwRj2N/ZkjDT+FkMT5pgedZoPlyhaG62J+9EgNIgUXE6IIL0b7bkLxCzhE6U4VQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.5.tgz", + "integrity": "sha512-ysEEU93Wv9p2UZBxTK3kUP7veHgyhTA0qYtI7bxK5EMXb3JxGv0D4IH54PxprAF26n+uHci24McVmzwIdLgvgQ==", "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/d3-hierarchy": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.3.tgz", - "integrity": "sha512-GpSK308Xj+HeLvogfEc7QsCOcIxkDwLhFYnOoohosEzOqv7/agxwvJER1v/kTC+CY1nfazR0F7gnHo7GE41/fw==" + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.4.tgz", + "integrity": "sha512-wrvjpRFdmEu6yAqgjGy8MSud9ggxJj+I9XLuztLeSf/E0j0j6RQYtxH2J8U0Cfbgiw9ZDHyhpmaVuWhxscYaAQ==" }, "node_modules/@types/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.2.tgz", + "integrity": "sha512-zAbCj9lTqW9J9PlF4FwnvEjXZUy75NQqPm7DMHZXuxCFTpuTrdK2NMYGQekf4hlasL78fCYOLu4EE3/tXElwow==", "dependencies": { "@types/d3-color": "*" } @@ -3186,9 +3623,9 @@ "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==" }, "node_modules/@types/d3-quadtree": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz", - "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.3.tgz", + "integrity": "sha512-GDWaR+rGEk4ToLQSGugYnoh9AYYblsg/8kmdpa1KAJMwcdZ0v8rwgnldURxI5UrzxPlCPzF7by/Tjmv+Jn21Dg==" }, "node_modules/@types/d3-random": { "version": "3.0.1", @@ -3196,9 +3633,9 @@ "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==" }, "node_modules/@types/d3-scale": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.4.tgz", - "integrity": "sha512-eq1ZeTj0yr72L8MQk6N6heP603ubnywSDRfNpi5enouR112HzGLS6RIvExCzZTraFF4HdzNpJMwA/zGiMoHUUw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.5.tgz", + "integrity": "sha512-w/C++3W394MHzcLKO2kdsIn5KKNTOqeQVzyPSGPLzQbkPw/jpeaGtSRlakcKevGgGsjJxGsbqS0fPrVFDbHrDA==", "dependencies": { "@types/d3-time": "*" } @@ -3209,27 +3646,27 @@ "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==" }, "node_modules/@types/d3-selection": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.6.tgz", - "integrity": "sha512-2ACr96USZVjXR9KMD9IWi1Epo4rSDKnUtYn6q2SPhYxykvXTw9vR77lkFNruXVg4i1tzQtBxeDMx0oNvJWbF1w==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.7.tgz", + "integrity": "sha512-qoj2O7KjfqCobmtFOth8FMvjwMVPUAAmn6xiUbLl1ld7vQCPgffvyV5BBcEFfqWdilAUm+3zciU/3P3vZrUMlg==" }, "node_modules/@types/d3-shape": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.2.tgz", - "integrity": "sha512-NN4CXr3qeOUNyK5WasVUV8NCSAx/CRVcwcb0BuuS1PiTqwIm6ABi1SyasLZ/vsVCFDArF+W4QiGzSry1eKYQ7w==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.3.tgz", + "integrity": "sha512-cHMdIq+rhF5IVwAV7t61pcEXfEHsEsrbBUPkFGBwTXuxtTAkBBrnrNA8++6OWm3jwVsXoZYQM8NEekg6CPJ3zw==", "dependencies": { "@types/d3-path": "*" } }, "node_modules/@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.1.tgz", + "integrity": "sha512-5j/AnefKAhCw4HpITmLDTPlf4vhi8o/dES+zbegfPb7LaGfNyqkLxBR6E+4yvTAgnJLmhe80EXFMzUs38fw4oA==" }, "node_modules/@types/d3-time-format": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz", - "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.1.tgz", + "integrity": "sha512-Br6EFeu9B1Zrem7KaYbr800xCmEDyq8uE60kEU8rWhC/XpFYX6ocGMZuRJDQfFCq6SyakQxNHFqIfJbFLf4x6Q==" }, "node_modules/@types/d3-timer": { "version": "3.0.0", @@ -3237,61 +3674,61 @@ "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" }, "node_modules/@types/d3-transition": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.4.tgz", - "integrity": "sha512-512a4uCOjUzsebydItSXsHrPeQblCVk8IKjqCUmrlvBWkkVh3donTTxmURDo1YPwIVDh5YVwCAO6gR4sgimCPQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.5.tgz", + "integrity": "sha512-dcfjP6prFxj3ziFOJrnt4W2P0oXNj/sGxsJXH8286sHtVZ4qWGbjuZj+RRCYx4YZ4C0izpeE8OqXVCtoWEtzYg==", "dependencies": { "@types/d3-selection": "*" } }, "node_modules/@types/d3-zoom": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.4.tgz", - "integrity": "sha512-cqkuY1ah9ZQre2POqjSLcM8g40UVya/qwEUrNYP2/rCVljbmqKCVcv+ebvwhlI5azIbSEL7m+os6n+WlYA43aA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.5.tgz", + "integrity": "sha512-mIefdTLtxuWUWTbBupCUXPAXVPmi8/Uwrq41gQpRh0rD25GMU1ku+oTELqNY2NuuiI0F3wXC5e1liBQi7YS7XQ==", "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "node_modules/@types/debug": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", - "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", + "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==" }, "node_modules/@types/geojson": { - "version": "7946.0.10", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + "version": "7946.0.11", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.11.tgz", + "integrity": "sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==" }, "node_modules/@types/hast": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.5.tgz", - "integrity": "sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.6.tgz", + "integrity": "sha512-47rJE80oqPmFdVDCD7IheXBrVdwuBgsYwoczFvKmwfo2Mzsnt+V9OONsYauFmICb6lQPpCuXYJWejBNs4pDJRg==", "dependencies": { "@types/unist": "^2" } }, "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==", "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", + "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", "dev": true }, "node_modules/@types/jest": { @@ -3305,9 +3742,9 @@ } }, "node_modules/@types/katex": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.2.tgz", - "integrity": "sha512-dHsSjSlU/EWEEbeNADr3FtZZOAXPkFPUO457QCnoNqcZQXNqNEu/svQd0Nritvd3wNff4vvC/f4e6xgX3Llt8A==" + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.3.tgz", + "integrity": "sha512-CeVMX9EhVUW8MWnei05eIRks4D5Wscw/W9Byz1s3PA+yJvcdvq9SaDjiUKvRvEgjpdTyJMjQA43ae4KTwsvOPg==" }, "node_modules/@types/keyv": { "version": "3.1.4", @@ -3319,9 +3756,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.198", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", - "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==", + "version": "4.14.199", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", + "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==", "dev": true }, "node_modules/@types/mathjax": { @@ -3343,9 +3780,9 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "16.18.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.50.tgz", - "integrity": "sha512-OiDU5xRgYTJ203v4cprTs0RwOCd5c5Zjv+K5P8KSqfiCsB1W3LcamTUMcnQarpq5kOYbhHfSOgIEJvdPyb5xyw==", + "version": "16.18.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.54.tgz", + "integrity": "sha512-oTmGy68gxZZ21FhTJVVvZBYpQHEBZxHKTsGshobMqm9qWpbqdZsA5jvsuPZcHu0KwpmLrOHWPdEfg7XDpNT9UA==", "devOptional": true }, "node_modules/@types/parse-json": { @@ -3354,14 +3791,14 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.7", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.7.tgz", + "integrity": "sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog==" }, "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "18.2.22", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.22.tgz", + "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3395,9 +3832,9 @@ } }, "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", + "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", @@ -3414,18 +3851,18 @@ "integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==" }, "node_modules/@types/uuid": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.3.tgz", - "integrity": "sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.4.tgz", + "integrity": "sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==", "dev": true }, "node_modules/@vitejs/plugin-react-swc": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz", - "integrity": "sha512-VJFWY5sfoZerQRvJrh518h3AcQt6f/yTuWn4/TRB+dqmYU0NX1qz7qM5Wfd+gOQqUzQW4gxKqKN3KpE/P3+zrA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.4.0.tgz", + "integrity": "sha512-m7UaA4Uvz82N/0EOVpZL4XsFIakRqrFKeSNxa1FBLSXGvWrWRBwmZb4qxk+ZIVAZcW3c3dn5YosomDgx62XWcQ==", "dev": true, "dependencies": { - "@swc/core": "^1.3.61" + "@swc/core": "^1.3.85" }, "peerDependencies": { "vite": "^4" @@ -3442,9 +3879,9 @@ "integrity": "sha512-jbQfFaw+57OBwPt7qSNHuW+RA8smmRwkWRS1Ozh6K/QxUspBgBV/LpdSzlY7vee8TomS6j3D33B9rIeH1qMwsA==" }, "node_modules/ace-builds": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.24.2.tgz", - "integrity": "sha512-zjvJiEw05BADxPMq5gN/Vgc9FgyiiDHpdy676Af1nJk8DF4ipgf0cNmV3wy8JHeQ6vezypEZV6pHQu9avO+WtQ==" + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.28.0.tgz", + "integrity": "sha512-wkJp+Wz8MRHtCVdt65L/jPFLAQ0iqJZ2EeD2XWOvKGbIi4mZNwHlpHRLRB8ZnQ07VoiB0TLFWwIjjm2FL9gUcQ==" }, "node_modules/acorn": { "version": "8.10.0", @@ -3635,9 +4072,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/autoprefixer": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", - "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "dev": true, "funding": [ { @@ -3655,8 +4092,8 @@ ], "dependencies": { "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001520", - "fraction.js": "^4.2.0", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -3943,9 +4380,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.21.11", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz", + "integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==", "funding": [ { "type": "opencollective", @@ -3961,10 +4398,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001538", + "electron-to-chromium": "^1.4.526", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -4079,9 +4516,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001534", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz", - "integrity": "sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==", + "version": "1.0.30001539", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001539.tgz", + "integrity": "sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==", "funding": [ { "type": "opencollective", @@ -4516,9 +4953,9 @@ } }, "node_modules/daisyui": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.7.4.tgz", - "integrity": "sha512-hAgTomIK8RDQ/RLH9Z2NxZiNVAO40w08FlhgYS/8CTFF+wggeHeNJ0qNBHWAJJzhjD8UU2u4PZ4nc4r9rwfTLw==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.8.0.tgz", + "integrity": "sha512-VsWD//XlHkOBFSiRNTOZTxTJ/W8xI65erowErfbDWrPTkMYqf0ee/FTaqn4rquZoCcumENdFegAL8eELMnztxQ==", "dev": true, "dependencies": { "colord": "^2.9", @@ -4789,10 +5226,19 @@ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.5.tgz", "integrity": "sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A==" }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.520", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.520.tgz", - "integrity": "sha512-Frfus2VpYADsrh1lB3v/ft/WVFlVzOIm+Q0p7U7VqHI6qr7NWHYKe+Wif3W50n7JAFoBsWVsoU0+qDks6WQ60g==" + "version": "1.4.529", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.529.tgz", + "integrity": "sha512-6uyPyXTo8lkv8SWAmjKFbG42U073TXlzD4R8rW3EzuznhFS2olCIAfjjQtV2dV2ar/vRF55KUd3zQYnCB0dd3A==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -5175,9 +5621,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -5266,9 +5712,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "hasInstallScript": true, "optional": true, "os": [ @@ -6491,6 +6937,14 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -7510,6 +7964,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -7897,12 +8360,12 @@ } }, "node_modules/playwright": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz", - "integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", "dev": true, "dependencies": { - "playwright-core": "1.38.0" + "playwright-core": "1.38.1" }, "bin": { "playwright": "cli.js" @@ -7915,9 +8378,9 @@ } }, "node_modules/playwright-core": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz", - "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -7926,24 +8389,10 @@ "node": ">=16" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", + "integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", "funding": [ { "type": "opencollective", @@ -8591,9 +9040,9 @@ } }, "node_modules/react-tooltip": { - "version": "5.21.3", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.3.tgz", - "integrity": "sha512-z3Q+Uka4D6uYxfsssPqfx1W8vw7NIHyC2ZMq+NJkWg4EpUD3w7Fwz/o+dezyUQMCHL7nO/2sFbtWIrkyxktq2Q==", + "version": "5.21.4", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.4.tgz", + "integrity": "sha512-LZsllEbiu63zNwuCalq3gIFcBu2Xf0I0fMg7uuF7/5ROo5//uHe8Sum7v9L1Rtp6IozcoU9YAjkNUZdrxutsNg==", "dependencies": { "@floating-ui/dom": "^1.0.0", "classnames": "^2.3.0" @@ -8619,24 +9068,24 @@ } }, "node_modules/react18-json-view": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/react18-json-view/-/react18-json-view-0.2.3.tgz", - "integrity": "sha512-FZlEWS6A3yf1CyuPRtxYrGNZVRvUT1jQvt/kS1C3SDrStwViENsAKhe3NANq37on9cPoNbLD8+KZB+EArNhuWQ==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/react18-json-view/-/react18-json-view-0.2.5.tgz", + "integrity": "sha512-BiCWyRUCVbnaK4kfNay8crOXZnWsZ6XsnY3fwOf5C+ZaY9w9FSTawo2p+h2UG/KcDP8meZuGlkP95klfFG9GfQ==", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/reactflow": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.8.3.tgz", - "integrity": "sha512-wuVxJOFqi1vhA4WAEJLK0JWx2TsTiWpxTXTRp/wvpqKInQgQcB49I2QNyNYsKJCQ6jjXektS7H+LXoaVK/pG4A==", + "version": "11.9.1", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.9.1.tgz", + "integrity": "sha512-lKWWWFne/o8/SShIeXXP4J/15384L0KDohH0It83gNKFgF6Yo7/EJE9p6k3CnYnOfyYETFRYb1KNOY04cSmtVA==", "dependencies": { - "@reactflow/background": "11.2.8", - "@reactflow/controls": "11.1.19", - "@reactflow/core": "11.8.3", - "@reactflow/minimap": "11.6.3", - "@reactflow/node-resizer": "2.1.5", - "@reactflow/node-toolbar": "1.2.7" + "@reactflow/background": "11.3.1", + "@reactflow/controls": "11.2.1", + "@reactflow/core": "11.9.1", + "@reactflow/minimap": "11.7.1", + "@reactflow/node-resizer": "2.2.1", + "@reactflow/node-toolbar": "1.3.1" }, "peerDependencies": { "react": ">=17", @@ -8840,9 +9289,9 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -8906,9 +9355,9 @@ } }, "node_modules/rollup": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.1.tgz", - "integrity": "sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg==", + "version": "3.29.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.3.tgz", + "integrity": "sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==", "bin": { "rollup": "dist/bin/rollup" }, @@ -9407,6 +9856,15 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -10121,9 +10579,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -10340,13 +10798,13 @@ } }, "node_modules/vite-plugin-svgr": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.2.0.tgz", - "integrity": "sha512-Uvq6niTvhqJU6ga78qLKBFJSDvxWhOnyfQSoKpDPMAGxJPo5S3+9hyjExE5YDj6Lpa4uaLkGc1cBgxXov+LjSw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.3.0.tgz", + "integrity": "sha512-vWZMCcGNdPqgziYFKQ3Y95XP0d0YGp28+MM3Dp9cTa/px5CKcHHrIoPl2Jw81rgVm6/ZUNONzjXbZQZ7Kw66og==", "dependencies": { - "@rollup/pluginutils": "^5.0.2", - "@svgr/core": "^7.0.0", - "@svgr/plugin-jsx": "^7.0.0" + "@rollup/pluginutils": "^5.0.4", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" }, "peerDependencies": { "vite": "^2.6.0 || 3 || 4" @@ -10871,9 +11329,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "engines": { "node": ">=10.0.0" }, @@ -10977,5 +11435,7336 @@ "url": "https://github.com/sponsors/wooorm" } } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "dev": true + }, + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" + }, + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@antfu/ni": { + "version": "0.21.8", + "resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-0.21.8.tgz", + "integrity": "sha512-90X8pU2szlvw0AJo9EZMbYc2eQKkmO7mAdC4tD4r5co2Mm56MT37MIG8EyB7p4WRheuzGxuLDxJ63mF6+Zajiw==" + }, + "@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "requires": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/compat-data": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==" + }, + "@babel/core": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "requires": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-module-transforms": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" + }, + "@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==" + }, + "@babel/helpers": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" + } + }, + "@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" + }, + "@babel/runtime": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/traverse": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "requires": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "requires": { + "@emotion/memoize": "^0.8.1" + } + }, + "@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "requires": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "@emotion/styled": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + } + }, + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, + "@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "optional": true + }, + "@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "requires": { + "@floating-ui/utils": "^0.1.3" + } + }, + "@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "requires": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "@floating-ui/react-dom": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", + "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", + "requires": { + "@floating-ui/dom": "^1.5.1" + } + }, + "@floating-ui/utils": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz", + "integrity": "sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==" + }, + "@headlessui/react": { + "version": "1.7.17", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz", + "integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==", + "requires": { + "client-only": "^0.0.1" + } + }, + "@heroicons/react": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", + "integrity": "sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==", + "requires": {} + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@mole-inc/bin-wrapper": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", + "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", + "dev": true, + "requires": { + "bin-check": "^4.1.0", + "bin-version-check": "^5.0.0", + "content-disposition": "^0.5.4", + "ext-name": "^5.0.0", + "file-type": "^17.1.6", + "filenamify": "^5.0.2", + "got": "^11.8.5", + "os-filter-obj": "^2.0.0" + } + }, + "@mui/base": { + "version": "5.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.17.tgz", + "integrity": "sha512-xNbk7iOXrglNdIxFBN0k3ySsPIFLWCnFxqsAYl7CIcDkD9low4kJ7IUuy6ctwx/HAy2fenrT3KXHr1sGjAMgpQ==", + "requires": { + "@babel/runtime": "^7.22.15", + "@floating-ui/react-dom": "^2.0.2", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.11", + "@popperjs/core": "^2.11.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1" + }, + "dependencies": { + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + } + } + }, + "@mui/core-downloads-tracker": { + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.11.tgz", + "integrity": "sha512-uY8FLQURhXe3f3O4dS5OSGML9KDm9+IE226cBu78jarVIzdQGPlXwGIlSI9VJR8MvZDA6C0+6XfWDhWCHruC5Q==" + }, + "@mui/material": { + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.11.tgz", + "integrity": "sha512-DnSdJzcR7lwG12JA5L2t8JF+RDzMygu5rCNW+logWb/KW2/TRzwLyVWO+CorHTBjBRd38DBxnwOCDiYkDd+N3A==", + "requires": { + "@babel/runtime": "^7.22.15", + "@mui/base": "5.0.0-beta.17", + "@mui/core-downloads-tracker": "^5.14.11", + "@mui/system": "^5.14.11", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.11", + "@types/react-transition-group": "^4.4.6", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "dependencies": { + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + } + } + }, + "@mui/private-theming": { + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.11.tgz", + "integrity": "sha512-MSnNNzTu9pfKLCKs1ZAKwOTgE4bz+fQA0fNr8Jm7NDmuWmw0CaN9Vq2/MHsatE7+S0A25IAKby46Uv1u53rKVQ==", + "requires": { + "@babel/runtime": "^7.22.15", + "@mui/utils": "^5.14.11", + "prop-types": "^15.8.1" + } + }, + "@mui/styled-engine": { + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.11.tgz", + "integrity": "sha512-jdUlqRgTYQ8RMtPX4MbRZqar6W2OiIb6J5KEFbIu4FqvPrk44Each4ppg/LAqp1qNlBYq5i+7Q10MYLMpDxX9A==", + "requires": { + "@babel/runtime": "^7.22.15", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + } + }, + "@mui/system": { + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.11.tgz", + "integrity": "sha512-yl8xV+y0k7j6dzBsHabKwoShmjqLa8kTxrhUI3JpqLG358VRVMJRW/ES0HhvfcCi4IVXde+Tc2P3K1akGL8zoA==", + "requires": { + "@babel/runtime": "^7.22.15", + "@mui/private-theming": "^5.14.11", + "@mui/styled-engine": "^5.14.11", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.11", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + }, + "dependencies": { + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + } + } + }, + "@mui/types": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", + "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", + "requires": {} + }, + "@mui/utils": { + "version": "5.14.11", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.11.tgz", + "integrity": "sha512-fmkIiCPKyDssYrJ5qk+dime1nlO3dmWfCtaPY/uVBqCRMBZ11JhddB9m8sjI2mgqQQwRJG5bq3biaosNdU/s4Q==", + "requires": { + "@babel/runtime": "^7.22.15", + "@types/prop-types": "^15.7.5", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@playwright/test": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", + "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", + "dev": true, + "requires": { + "playwright": "1.38.1" + } + }, + "@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" + }, + "@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-accordion": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz", + "integrity": "sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collapsible": "1.0.3", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/react-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz", + "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + } + }, + "@radix-ui/react-collapsible": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", + "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + } + }, + "@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + } + }, + "@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + } + }, + "@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + } + }, + "@radix-ui/react-dropdown-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", + "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + } + }, + "@radix-ui/react-form": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.0.3.tgz", + "integrity": "sha512-kgE+Z/haV6fxE5WqIXj05KkaXa3OkZASoTDy25yX2EIp/x0c54rOH/vFr5nOZTg7n7T1z8bSyXmiVIFP9bbhPQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-label": "2.0.2", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "requires": {} + }, + "@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + } + }, + "@radix-ui/react-label": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", + "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/react-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + } + }, + "@radix-ui/react-menubar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.0.4.tgz", + "integrity": "sha512-bHgUo9gayKZfaQcWSSLr++LyS0rgh+MvD89DE4fJ6TkGHvjHgPaBZf44hdka7ogOxIOdj9163J+5xL2Dn4qzzg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-popover": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", + "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + } + }, + "@radix-ui/react-popper": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "requires": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + } + }, + "@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + } + }, + "@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + } + }, + "@radix-ui/react-progress": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.0.3.tgz", + "integrity": "sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/react-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-select": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", + "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "dependencies": { + "@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + } + }, + "@radix-ui/react-focus-scope": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", + "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + } + }, + "@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + } + }, + "@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + } + } + } + }, + "@radix-ui/react-separator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz", + "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + } + }, + "@radix-ui/react-switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz", + "integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + } + }, + "@radix-ui/react-tabs": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz", + "integrity": "sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-use-controllable-state": "1.0.1" + } + }, + "@radix-ui/react-tooltip": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", + "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + } + }, + "@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + } + }, + "@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + } + }, + "@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + } + }, + "@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + } + }, + "@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + } + }, + "@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, + "@reactflow/background": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.1.tgz", + "integrity": "sha512-FjYHXvzwwaCJJS5rRxXbU4r8xbAqEvLQkO1l4czjWgrE0VQELC93RKVI4lRpkv2RkkIwRNMvoGb6kXNbJWiU/g==", + "requires": { + "@reactflow/core": "11.9.1", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + } + }, + "@reactflow/controls": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.1.tgz", + "integrity": "sha512-mW8kz/J77tc5uRwSTSUH1OtkksaVc52Dbz7aTuF0aKZXwPHoAfRPFeet2VLQV1IdKeSab82Mqy0WTSJj6Hl15w==", + "requires": { + "@reactflow/core": "11.9.1", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + } + }, + "@reactflow/core": { + "version": "11.9.1", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.9.1.tgz", + "integrity": "sha512-qXWwB2KSWkp3MH3fhY1HL4yFBvMZ+7y9mkfk0we7wCuGNgQzWVJDwvVEpPTkyp5xyxX53sSF3UDC+UaFCB4ElA==", + "requires": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + } + }, + "@reactflow/minimap": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.1.tgz", + "integrity": "sha512-ZNHx1wURS055kYYaiFhrmGYI2UXA7Cwcjxfww4Mq3PWvIIaEFL9t34aUWoXCYm+NPFHElwC8jM7Vyp7xDR9oyg==", + "requires": { + "@reactflow/core": "11.9.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + } + }, + "@reactflow/node-resizer": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.1.tgz", + "integrity": "sha512-nUh73WHVAYsJLvgSKqKAxIPU9vUkEM5iold2zQsEOHqjfnk86m1r33fTEiw5+MPqCFHMoGzyZe2gxXUMFqg/3A==", + "requires": { + "@reactflow/core": "11.9.1", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + } + }, + "@reactflow/node-toolbar": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.1.tgz", + "integrity": "sha512-YkrpB2+/9ymZ2h1nmuKytmrFlIXdiDzeDqmZnyJF2UNH/4sSo/GswPJmqiA8ETOP2oEdIkNmO9dwVpiDvWtZ5A==", + "requires": { + "@reactflow/core": "11.9.1", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + } + }, + "@remix-run/router": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz", + "integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA==" + }, + "@rollup/pluginutils": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz", + "integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==", + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "requires": {} + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "requires": {} + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "requires": {} + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "requires": {} + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "requires": {} + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "requires": {} + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "requires": {} + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "requires": {} + }, + "@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + } + }, + "@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "dependencies": { + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } + } + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "requires": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "dependencies": { + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + } + } + }, + "@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + } + }, + "@swc/cli": { + "version": "0.1.62", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.62.tgz", + "integrity": "sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==", + "dev": true, + "requires": { + "@mole-inc/bin-wrapper": "^8.0.1", + "commander": "^7.1.0", + "fast-glob": "^3.2.5", + "semver": "^7.3.8", + "slash": "3.0.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, + "@swc/core": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.89.tgz", + "integrity": "sha512-+FchWateF57g50ChX6++QQDwgVd6iWZX5HA6m9LRIdJIB56bIqbwRQDwVL3Q8Rlbry4kmw+RxiOW2FjAx9mQOQ==", + "dev": true, + "requires": { + "@swc/core-darwin-arm64": "1.3.89", + "@swc/core-darwin-x64": "1.3.89", + "@swc/core-linux-arm-gnueabihf": "1.3.89", + "@swc/core-linux-arm64-gnu": "1.3.89", + "@swc/core-linux-arm64-musl": "1.3.89", + "@swc/core-linux-x64-gnu": "1.3.89", + "@swc/core-linux-x64-musl": "1.3.89", + "@swc/core-win32-arm64-msvc": "1.3.89", + "@swc/core-win32-ia32-msvc": "1.3.89", + "@swc/core-win32-x64-msvc": "1.3.89", + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + } + }, + "@swc/core-darwin-arm64": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.89.tgz", + "integrity": "sha512-LVCZQ2yGrX2678uMvW66IF1bzcOMqiABi+ioNDnJtAIsE/zRVMEYp1ivbOrH32FmPplBby6CGgJIOT3P4VaP1g==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.89.tgz", + "integrity": "sha512-IwKlX65YrPBF3urOxBJia0PjnZeaICnCkSwGLiYyV1RhM8XwZ/XyEDTBEsdph3WxUM5wCZQSk8UY/d0saIsX9w==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.89.tgz", + "integrity": "sha512-u5qAPh7NkKoDJYwfaB5zuRvzW2+A89CQQHp5xcYjpctRsk3sUrPmC7vNeE12xipBNKLujIG59ppbrf6Pkp5XIg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.89.tgz", + "integrity": "sha512-eykuO7XtPltk600HvnnRr1nU5qGk7PeqLmztHA7R2bu2SbtcbCGsewPNcAX5eP8by2VwpGcLPdxaKyqeUwCgoA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.89.tgz", + "integrity": "sha512-i/65Vt3ljfd6EyR+WWZ5aAjZLTQMIHoR+Ay97jE0kysRn8MEOINu0SWyiEwcdXzRGlt+zkrKYfOxp745sWPDAw==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.89.tgz", + "integrity": "sha512-ERETXe68CJRdNkL3EIN62gErh3p6+/6hmz4C0epnYJ4F7QspdW/EOluL1o9bl4dux4Xz0nmBPSZsqfHq/nl1KA==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.89.tgz", + "integrity": "sha512-EXiwgU5E/yC5zuJtOXXWv+wMwpe5DR380XhVxIOBG6nFi6MR3O2X37KxeEdQZX8RwN7/KU6kNHeifzEiSvixfA==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.89.tgz", + "integrity": "sha512-j7GvkgeOrZlB55MpEwX+6E6KjxwOmwRXpIqMjF11JDIZ0wEwHlBxZhlnQQ58iuI6jL6AJgDH/ktDhMyELoBiHw==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.89.tgz", + "integrity": "sha512-n57nE7d3FXBa3Y2+VoJdPulcUAS0ZGAGVGxFpeM/tZt1MBEN5OvpOSOIp35dK5HAAxAzTPlmqj9KUYnVxLMVKw==", + "dev": true, + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.3.89", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.89.tgz", + "integrity": "sha512-6yMAmqgseAwEXFIwurP7CL8yIH8n7/Rg62ooOVSLSWL5O/Pwlpy1WrpoA0eKhgMLLkIrPvNuKaE/rG7c2iNQHA==", + "dev": true, + "optional": true + }, + "@swc/counter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.1.tgz", + "integrity": "sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==", + "dev": true + }, + "@swc/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tabler/icons": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.35.0.tgz", + "integrity": "sha512-qW/itKdmFvfGw6mAQ+cZy+2MYTXb0XdGAVhO3obYLJEfsSPMwQRO0S9ckFk1xMQX/Tj7REC3TEmWUBWNi3/o3g==" + }, + "@tabler/icons-react": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.35.0.tgz", + "integrity": "sha512-jK2zgtMF2+LSjtbjYsfer+ryz72TwyJqv3MsmCfEpQwP37u01xvmTlVhJ+ox3bV+trsgjojsldVDuB05JuXLaw==", + "requires": { + "@tabler/icons": "2.35.0", + "prop-types": "^15.7.2" + } + }, + "@tailwindcss/forms": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.6.tgz", + "integrity": "sha512-Fw+2BJ0tmAwK/w01tEFL5TiaJBX1NLT1/YbWgvm7ws3Qcn11kiXxzNTEQDMs5V3mQemhB56l3u0i9dwdzSQldA==", + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, + "@tailwindcss/line-clamp": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz", + "integrity": "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==", + "requires": {} + }, + "@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dev": true, + "requires": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + } + }, + "@testing-library/dom": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", + "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + }, + "@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "dependencies": { + "@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + } + } + }, + "@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5" + } + }, + "@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" + }, + "@ts-morph/common": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.19.0.tgz", + "integrity": "sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==", + "requires": { + "fast-glob": "^3.2.12", + "minimatch": "^7.4.3", + "mkdirp": "^2.1.6", + "path-browserify": "^1.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@types/aria-query": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.2.tgz", + "integrity": "sha512-PHKZuMN+K5qgKIWhBodXzQslTo5P+K/6LqeKXS6O/4liIDdZqaX5RXrCK++LAw+y/nptN48YmUMFiQHRSWYwtQ==", + "dev": true + }, + "@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", + "requires": { + "axios": "*" + } + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, + "@types/d3": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.1.tgz", + "integrity": "sha512-lBpYmbHTCtFKO1DB1R7E9dXp9/g1F3JXSGOF7iKPZ+wRmYg/Q6tCRHODGOc5Qk25fJRe2PI60EDRf2HLPUncMA==", + "requires": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "@types/d3-array": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.8.tgz", + "integrity": "sha512-2xAVyAUgaXHX9fubjcCbGAUOqYfRJN1em1EKR2HfzWBpObZhwfnZKvofTN4TplMqJdFQao61I+NVSai/vnBvDQ==" + }, + "@types/d3-axis": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.4.tgz", + "integrity": "sha512-ySnjI/7qm+J602VjcejXcqs1hEuu5UBbGaJGp+Cn/yKVc1iS3JueLVpToGdQsS2sqta7tqA/kG4ore/+LH90UA==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-brush": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.4.tgz", + "integrity": "sha512-Kg5uIsdJNMCs5lTqeZFsTKqj9lBvpiFRDkYN3j2CDlPhonNDg9/gXVpv1E/MKh3tEqArryIj9o6RBGE/MQe+6Q==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-chord": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.4.tgz", + "integrity": "sha512-p4PvN1N+7GL3Y/NI9Ug1TKwowUV6h664kmxL79ctp1HRYCk1mhP0+SXhjRsoWXCdnJfbLLLmpV99rt8dMrHrzg==" + }, + "@types/d3-color": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.1.tgz", + "integrity": "sha512-CSAVrHAtM9wfuLJ2tpvvwCU/F22sm7rMHNN+yh9D6O6hyAms3+O0cgMpC1pm6UEUMOntuZC8bMt74PteiDUdCg==" + }, + "@types/d3-contour": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.4.tgz", + "integrity": "sha512-B0aeX8Xg3MNUglULxqDvlgY1SVXuN2xtEleYSAY0iMhl/SMVT7snzgAveejjwM3KaWuNXIoXEJ7dmXE8oPq/jA==", + "requires": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "@types/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-WplUJ/OHU7eITneDqNnzK+2pgR+WDzUHG6XAUVo+oWHPQq74VcgUdw8a4ODweaZzF56OVYK+x9GxCyuq6hSu1A==" + }, + "@types/d3-dispatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.4.tgz", + "integrity": "sha512-NApHpGHRNxUy7e2Lfzl/cwOucmn4Xdx6FdmXzAoomo8T81LyGmlBjjko/vP0TVzawlvEFLDq8OCRLulW6DDzKw==" + }, + "@types/d3-drag": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.4.tgz", + "integrity": "sha512-/t53K1erTuUbP7WIX9SE0hlmytpTYRbIthlhbGkBHzCV5vPO++7yrk8OlisWPyIJO5TGowTmqCtGH2tokY5T/g==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-dsv": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.4.tgz", + "integrity": "sha512-YxfUVJ55HxR8oq88136w09mBMPNhgH7PZjteq72onWXWOohGif/cLQnQv8V4A5lEGjXF04LhwSTpmzpY9wyVyA==" + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "@types/d3-fetch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.4.tgz", + "integrity": "sha512-RleYajubALkGjrvatxWhlygfvB1KNF0Uzz9guRUeeA+M/2B7l8rxObYdktaX9zU1st04lMCHjZWe4vbl+msH2Q==", + "requires": { + "@types/d3-dsv": "*" + } + }, + "@types/d3-force": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.6.tgz", + "integrity": "sha512-G9wbOvCxkNlLrppoHLZ6oFpbm3z7ibfkXwLD8g5/4Aa7iTEV0Z7TQ0OL8UxAtvdOhCa2VZcSuqn1NQqyCEqmiw==" + }, + "@types/d3-format": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.2.tgz", + "integrity": "sha512-9oQWvKk2qVBo49FQq8yD/et8Lx0W5Ac2FdGSOUecqOFKqh0wkpyHqf9Qc7A06ftTR+Lz13Pi3jHIQis0aCueOA==" + }, + "@types/d3-geo": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.5.tgz", + "integrity": "sha512-ysEEU93Wv9p2UZBxTK3kUP7veHgyhTA0qYtI7bxK5EMXb3JxGv0D4IH54PxprAF26n+uHci24McVmzwIdLgvgQ==", + "requires": { + "@types/geojson": "*" + } + }, + "@types/d3-hierarchy": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.4.tgz", + "integrity": "sha512-wrvjpRFdmEu6yAqgjGy8MSud9ggxJj+I9XLuztLeSf/E0j0j6RQYtxH2J8U0Cfbgiw9ZDHyhpmaVuWhxscYaAQ==" + }, + "@types/d3-interpolate": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.2.tgz", + "integrity": "sha512-zAbCj9lTqW9J9PlF4FwnvEjXZUy75NQqPm7DMHZXuxCFTpuTrdK2NMYGQekf4hlasL78fCYOLu4EE3/tXElwow==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "@types/d3-polygon": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz", + "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==" + }, + "@types/d3-quadtree": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.3.tgz", + "integrity": "sha512-GDWaR+rGEk4ToLQSGugYnoh9AYYblsg/8kmdpa1KAJMwcdZ0v8rwgnldURxI5UrzxPlCPzF7by/Tjmv+Jn21Dg==" + }, + "@types/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==" + }, + "@types/d3-scale": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.5.tgz", + "integrity": "sha512-w/C++3W394MHzcLKO2kdsIn5KKNTOqeQVzyPSGPLzQbkPw/jpeaGtSRlakcKevGgGsjJxGsbqS0fPrVFDbHrDA==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==" + }, + "@types/d3-selection": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.7.tgz", + "integrity": "sha512-qoj2O7KjfqCobmtFOth8FMvjwMVPUAAmn6xiUbLl1ld7vQCPgffvyV5BBcEFfqWdilAUm+3zciU/3P3vZrUMlg==" + }, + "@types/d3-shape": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.3.tgz", + "integrity": "sha512-cHMdIq+rhF5IVwAV7t61pcEXfEHsEsrbBUPkFGBwTXuxtTAkBBrnrNA8++6OWm3jwVsXoZYQM8NEekg6CPJ3zw==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.1.tgz", + "integrity": "sha512-5j/AnefKAhCw4HpITmLDTPlf4vhi8o/dES+zbegfPb7LaGfNyqkLxBR6E+4yvTAgnJLmhe80EXFMzUs38fw4oA==" + }, + "@types/d3-time-format": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.1.tgz", + "integrity": "sha512-Br6EFeu9B1Zrem7KaYbr800xCmEDyq8uE60kEU8rWhC/XpFYX6ocGMZuRJDQfFCq6SyakQxNHFqIfJbFLf4x6Q==" + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, + "@types/d3-transition": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.5.tgz", + "integrity": "sha512-dcfjP6prFxj3ziFOJrnt4W2P0oXNj/sGxsJXH8286sHtVZ4qWGbjuZj+RRCYx4YZ4C0izpeE8OqXVCtoWEtzYg==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-zoom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.5.tgz", + "integrity": "sha512-mIefdTLtxuWUWTbBupCUXPAXVPmi8/Uwrq41gQpRh0rD25GMU1ku+oTELqNY2NuuiI0F3wXC5e1liBQi7YS7XQ==", + "requires": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "@types/debug": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", + "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", + "requires": { + "@types/ms": "*" + } + }, + "@types/estree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==" + }, + "@types/geojson": { + "version": "7946.0.11", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.11.tgz", + "integrity": "sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==" + }, + "@types/hast": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.6.tgz", + "integrity": "sha512-47rJE80oqPmFdVDCD7IheXBrVdwuBgsYwoczFvKmwfo2Mzsnt+V9OONsYauFmICb6lQPpCuXYJWejBNs4pDJRg==", + "requires": { + "@types/unist": "^2" + } + }, + "@types/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", + "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", + "dev": true + }, + "@types/jest": { + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", + "dev": true, + "requires": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "@types/katex": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.3.tgz", + "integrity": "sha512-CeVMX9EhVUW8MWnei05eIRks4D5Wscw/W9Byz1s3PA+yJvcdvq9SaDjiUKvRvEgjpdTyJMjQA43ae4KTwsvOPg==" + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lodash": { + "version": "4.14.199", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz", + "integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==", + "dev": true + }, + "@types/mathjax": { + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@types/mathjax/-/mathjax-0.0.37.tgz", + "integrity": "sha512-y0WSZBtBNQwcYipTU/BhgeFu1EZNlFvUNCmkMXV9kBQZq7/o5z82dNVyH3yy2Xv5zzeNeQoHSL4Xm06+EQiH+g==" + }, + "@types/mdast": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", + "integrity": "sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==", + "requires": { + "@types/unist": "^2" + } + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "@types/node": { + "version": "16.18.54", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.54.tgz", + "integrity": "sha512-oTmGy68gxZZ21FhTJVVvZBYpQHEBZxHKTsGshobMqm9qWpbqdZsA5jvsuPZcHu0KwpmLrOHWPdEfg7XDpNT9UA==", + "devOptional": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/prop-types": { + "version": "15.7.7", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.7.tgz", + "integrity": "sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog==" + }, + "@types/react": { + "version": "18.2.22", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.22.tgz", + "integrity": "sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "devOptional": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "requires": { + "@types/react": "*" + } + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/scheduler": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", + "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" + }, + "@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "dev": true, + "requires": { + "@types/jest": "*" + } + }, + "@types/unist": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz", + "integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==" + }, + "@types/uuid": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.4.tgz", + "integrity": "sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==", + "dev": true + }, + "@vitejs/plugin-react-swc": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.4.0.tgz", + "integrity": "sha512-m7UaA4Uvz82N/0EOVpZL4XsFIakRqrFKeSNxa1FBLSXGvWrWRBwmZb4qxk+ZIVAZcW3c3dn5YosomDgx62XWcQ==", + "dev": true, + "requires": { + "@swc/core": "^1.3.85" + } + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "accordion": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/accordion/-/accordion-3.0.2.tgz", + "integrity": "sha512-jbQfFaw+57OBwPt7qSNHuW+RA8smmRwkWRS1Ozh6K/QxUspBgBV/LpdSzlY7vee8TomS6j3D33B9rIeH1qMwsA==" + }, + "ace-builds": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.28.0.tgz", + "integrity": "sha512-wkJp+Wz8MRHtCVdt65L/jPFLAQ0iqJZ2EeD2XWOvKGbIi4mZNwHlpHRLRB8ZnQ07VoiB0TLFWwIjjm2FL9gUcQ==" + }, + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "add": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/add/-/add-2.0.6.tgz", + "integrity": "sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansi-to-html": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", + "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", + "requires": { + "entities": "^2.2.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "requires": { + "tslib": "^2.0.0" + } + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "requires": { + "deep-equal": "^2.0.5" + } + }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "autoprefixer": { + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "dev": true, + "requires": { + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "axios": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bin-check": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", + "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "executable": "^4.1.0" + } + }, + "bin-version": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", + "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "find-versions": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "bin-version-check": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", + "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", + "dev": true, + "requires": { + "bin-version": "^6.0.0", + "semver": "^7.5.3", + "semver-truncate": "^3.0.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "requires": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.11", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz", + "integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==", + "requires": { + "caniuse-lite": "^1.0.30001538", + "electron-to-chromium": "^1.4.526", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-lite": { + "version": "1.0.30001539", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001539.tgz", + "integrity": "sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==" + }, + "ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "class-variance-authority": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.1.tgz", + "integrity": "sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==", + "requires": { + "clsx": "1.2.1" + } + }, + "classcat": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.4.tgz", + "integrity": "sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==" + }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "requires": { + "restore-cursor": "^4.0.0" + } + }, + "cli-spinners": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==" + }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, + "code-block-writer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" + }, + "d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + } + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, + "d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "requires": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + } + }, + "daisyui": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.8.0.tgz", + "integrity": "sha512-VsWD//XlHkOBFSiRNTOZTxTJ/W8xI65erowErfbDWrPTkMYqf0ee/FTaqn4rquZoCcumENdFegAL8eELMnztxQ==", + "dev": true, + "requires": { + "colord": "^2.9", + "css-selector-tokenizer": "^0.8", + "postcss": "^8", + "postcss-js": "^4", + "tailwindcss": "^3" + } + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "requires": { + "character-entities": "^2.0.0" + }, + "dependencies": { + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + } + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "deep-equal": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + } + }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" + }, + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A==" + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "electron-to-chromium": { + "version": "1.4.529", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.529.tgz", + "integrity": "sha512-6uyPyXTo8lkv8SWAmjKFbG42U073TXlzD4R8rW3EzuznhFS2olCIAfjjQtV2dV2ar/vRF55KUd3zQYnCB0dd3A==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + } + }, + "esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "requires": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + } + }, + "ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "dev": true, + "requires": { + "mime-db": "^1.28.0" + } + }, + "ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "dev": true, + "requires": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, + "file-type": { + "version": "17.1.6", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", + "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", + "dev": true, + "requires": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0-alpha.9", + "token-types": "^5.0.0-alpha.2" + } + }, + "filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "dev": true + }, + "filenamify": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", + "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", + "dev": true, + "requires": { + "filename-reserved-regex": "^3.0.0", + "strip-outer": "^2.0.0", + "trim-repeated": "^2.0.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "find-versions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", + "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", + "dev": true, + "requires": { + "semver-regex": "^4.0.5" + } + }, + "follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "fraction.js": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz", + "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==", + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + } + }, + "get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==" + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hast-util-from-dom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz", + "integrity": "sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==", + "requires": { + "hastscript": "^7.0.0", + "web-namespaces": "^2.0.0" + }, + "dependencies": { + "hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "requires": { + "@types/hast": "^2.0.0" + } + }, + "hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + } + } + } + }, + "hast-util-is-element": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz", + "integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==", + "requires": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0" + } + }, + "hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + }, + "hast-util-to-text": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz", + "integrity": "sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "hast-util-is-element": "^2.0.0", + "unist-util-find-after": "^4.0.0" + } + }, + "hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==" + }, + "hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "dependencies": { + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + }, + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "requires": { + "xtend": "^4.0.0" + } + }, + "space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + } + } + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, + "is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" + }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.11" + } + }, + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, + "is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jiti": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", + "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==", + "requires": { + "commander": "^8.3.0" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + } + } + }, + "keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" + }, + "log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "requires": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + } + } + }, + "longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "lucide-react": { + "version": "0.233.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.233.0.tgz", + "integrity": "sha512-r0jMHF0vPDq2wBbZ0B3rtIcBjDyWDKpHu+vAjD2OHn2WLUr3HN5IHovtO0EMgQXuSI7YrMZbjsEZWC2uBHr8nQ==", + "requires": {} + }, + "lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true + }, + "markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==" + }, + "mathjax-full": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.2.tgz", + "integrity": "sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==", + "requires": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, + "mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-find-and-replace": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", + "integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==", + "requires": { + "@types/mdast": "^3.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" + } + } + }, + "mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-gfm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz", + "integrity": "sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==", + "requires": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-gfm-autolink-literal": "^1.0.0", + "mdast-util-gfm-footnote": "^1.0.0", + "mdast-util-gfm-strikethrough": "^1.0.0", + "mdast-util-gfm-table": "^1.0.0", + "mdast-util-gfm-task-list-item": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + } + }, + "mdast-util-gfm-autolink-literal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz", + "integrity": "sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==", + "requires": { + "@types/mdast": "^3.0.0", + "ccount": "^2.0.0", + "mdast-util-find-and-replace": "^2.0.0", + "micromark-util-character": "^1.0.0" + } + }, + "mdast-util-gfm-footnote": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz", + "integrity": "sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-util-normalize-identifier": "^1.0.0" + } + }, + "mdast-util-gfm-strikethrough": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz", + "integrity": "sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-gfm-table": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz", + "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==", + "requires": { + "@types/mdast": "^3.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-gfm-task-list-item": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz", + "integrity": "sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-math": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz", + "integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==", + "requires": { + "@types/mdast": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + } + }, + "mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "requires": { + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" + } + }, + "mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "requires": { + "@types/mdast": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==" + }, + "micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz", + "integrity": "sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==", + "requires": { + "micromark-extension-gfm-autolink-literal": "^1.0.0", + "micromark-extension-gfm-footnote": "^1.0.0", + "micromark-extension-gfm-strikethrough": "^1.0.0", + "micromark-extension-gfm-table": "^1.0.0", + "micromark-extension-gfm-tagfilter": "^1.0.0", + "micromark-extension-gfm-task-list-item": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-gfm-autolink-literal": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz", + "integrity": "sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-gfm-footnote": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz", + "integrity": "sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==", + "requires": { + "micromark-core-commonmark": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-strikethrough": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz", + "integrity": "sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-table": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz", + "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-gfm-tagfilter": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz", + "integrity": "sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==", + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-gfm-task-list-item": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz", + "integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-math": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.2.tgz", + "integrity": "sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==", + "requires": { + "@types/katex": "^0.16.0", + "katex": "^0.16.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==" + }, + "micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==" + }, + "micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==" + }, + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "mj-context-menu": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", + "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==" + }, + "mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==" + }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "ora": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.1.tgz", + "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", + "requires": { + "chalk": "^5.0.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.6.1", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.1.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "strip-ansi": "^7.0.1", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + } + } + }, + "os-filter-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", + "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", + "dev": true, + "requires": { + "arch": "^2.1.0" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + }, + "dependencies": { + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + } + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" + }, + "playwright": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.38.1" + } + }, + "playwright-core": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", + "dev": true + }, + "postcss": { + "version": "8.4.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", + "integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "dependencies": { + "yaml": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==" + } + } + }, + "postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "requires": { + "postcss-selector-parser": "^6.0.11" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "prettier-plugin-organize-imports": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.3.tgz", + "integrity": "sha512-KFvk8C/zGyvUaE3RvxN2MhCLwzV6OBbFSkwZ2OamCrs9ZY4i5L77jQ/w4UmUr+lqX8qbaqVq6bZZkApn+IgJSg==", + "dev": true, + "requires": {} + }, + "prettier-plugin-tailwindcss": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.3.0.tgz", + "integrity": "sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==", + "dev": true, + "requires": {} + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + } + } + }, + "prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "property-information": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.3.0.tgz", + "integrity": "sha512-gVNZ74nqhRMiIUYWGQdosYetaKc83x8oT41a0LlV3AAFCAZwCpg4vmGkq8t34+cUhp3cnM4XDiU/7xlgK7HGrg==" + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-ace": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", + "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", + "requires": { + "ace-builds": "^1.4.14", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.7.2" + } + }, + "react-cookie": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz", + "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==", + "requires": { + "@types/hoist-non-react-statics": "^3.0.1", + "hoist-non-react-statics": "^3.0.0", + "universal-cookie": "^4.0.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-error-boundary": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz", + "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, + "react-icons": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", + "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "requires": {} + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "react-laag": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/react-laag/-/react-laag-2.0.5.tgz", + "integrity": "sha512-RCvublJhdcgGRHU1wMYJ8kRtnYsKUgYusLvVhMuftg65POnnOB4+fwXvnETm6adc0cMnc1spujlrK6bGIz6aug==", + "requires": { + "tiny-warning": "^1.0.3" + } + }, + "react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "requires": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + } + }, + "react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "requires": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + } + }, + "react-remove-scroll-bar": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", + "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", + "requires": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + } + }, + "react-router": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz", + "integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==", + "requires": { + "@remix-run/router": "1.9.0" + } + }, + "react-router-dom": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz", + "integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==", + "requires": { + "@remix-run/router": "1.9.0", + "react-router": "6.16.0" + } + }, + "react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "requires": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + } + }, + "react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "requires": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + } + }, + "react-tabs": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.0.2.tgz", + "integrity": "sha512-aQXTKolnM28k3KguGDBSAbJvcowOQr23A+CUJdzJtOSDOtTwzEaJA+1U4KwhNL9+Obe+jFS7geuvA7ICQPXOnQ==", + "requires": { + "clsx": "^2.0.0", + "prop-types": "^15.5.0" + }, + "dependencies": { + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + } + } + }, + "react-tooltip": { + "version": "5.21.4", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.4.tgz", + "integrity": "sha512-LZsllEbiu63zNwuCalq3gIFcBu2Xf0I0fMg7uuF7/5ROo5//uHe8Sum7v9L1Rtp6IozcoU9YAjkNUZdrxutsNg==", + "requires": { + "@floating-ui/dom": "^1.0.0", + "classnames": "^2.3.0" + } + }, + "react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "react18-json-view": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/react18-json-view/-/react18-json-view-0.2.5.tgz", + "integrity": "sha512-BiCWyRUCVbnaK4kfNay8crOXZnWsZ6XsnY3fwOf5C+ZaY9w9FSTawo2p+h2UG/KcDP8meZuGlkP95klfFG9GfQ==", + "requires": {} + }, + "reactflow": { + "version": "11.9.1", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.9.1.tgz", + "integrity": "sha512-lKWWWFne/o8/SShIeXXP4J/15384L0KDohH0It83gNKFgF6Yo7/EJE9p6k3CnYnOfyYETFRYb1KNOY04cSmtVA==", + "requires": { + "@reactflow/background": "11.3.1", + "@reactflow/controls": "11.2.1", + "@reactflow/core": "11.9.1", + "@reactflow/minimap": "11.7.1", + "@reactflow/node-resizer": "2.2.1", + "@reactflow/node-toolbar": "1.3.1" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dev": true, + "requires": { + "readable-stream": "^3.6.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "requires": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "dependencies": { + "prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" + } + } + }, + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + } + }, + "rehype-mathjax": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/rehype-mathjax/-/rehype-mathjax-4.0.3.tgz", + "integrity": "sha512-QIwWH9U+r54nMQklVkT1qluxhKyzdPWz9dFwgel3BrseQsWZafRTDTUj8VR8/14nFuRIV2ChuCMz4zpACPoYvg==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mathjax": "^0.0.37", + "hast-util-from-dom": "^4.0.0", + "hast-util-to-text": "^3.1.0", + "jsdom": "^20.0.0", + "mathjax-full": "^3.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-gfm": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", + "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-gfm": "^2.0.0", + "micromark-extension-gfm": "^2.0.0", + "unified": "^10.0.0" + } + }, + "remark-math": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz", + "integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-math": "^2.0.0", + "micromark-extension-math": "^2.0.0", + "unified": "^10.0.0" + } + }, + "remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve": { + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rollup": { + "version": "3.29.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.3.tgz", + "integrity": "sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==", + "requires": { + "fsevents": "~2.3.2" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "requires": { + "mri": "^1.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true + }, + "semver-truncate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", + "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + }, + "set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + } + }, + "shadcn-ui": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/shadcn-ui/-/shadcn-ui-0.2.3.tgz", + "integrity": "sha512-Bf2y8d5VusDbCs5l/SRmFTJ2tW9oBvIDWL1xmmURKuxn8rznXApoTdflPJ+Q68zKFXmIh6cYd4cJ8I8nzOkoAQ==", + "requires": { + "@antfu/ni": "^0.21.4", + "chalk": "5.2.0", + "commander": "^10.0.0", + "cosmiconfig": "^8.1.3", + "diff": "^5.1.0", + "execa": "^7.0.0", + "fs-extra": "^11.1.0", + "https-proxy-agent": "^6.2.0", + "node-fetch": "^3.3.0", + "ora": "^6.1.2", + "prompts": "^2.4.2", + "ts-morph": "^18.0.0", + "tsconfig-paths": "^4.2.0", + "zod": "^3.20.2" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "requires": { + "debug": "^4.3.4" + } + }, + "chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "https-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz", + "integrity": "sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==" + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==" + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" + } + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "short-unique-id": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz", + "integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "dev": true, + "requires": { + "sort-keys": "^1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + }, + "speech-rule-engine": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", + "integrity": "sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==", + "requires": { + "commander": "9.2.0", + "wicked-good-xpath": "1.3.0", + "xmldom-sre": "0.1.31" + }, + "dependencies": { + "commander": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz", + "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==" + } + } + }, + "stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "requires": { + "bl": "^5.0.0" + } + }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "requires": { + "internal-slot": "^1.0.4" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-outer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", + "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", + "dev": true + }, + "strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dev": true, + "requires": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + } + }, + "style-to-object": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.2.tgz", + "integrity": "sha512-1JGpfPB3lo42ZX8cuPrheZbfQ6kqPPnPHlKMyeRYtfKD+0jG+QsXgXN57O/dvJlzlB2elI6dGmrPnl5VPQFPaA==", + "requires": { + "inline-style-parser": "0.1.1" + } + }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "switch": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/switch/-/switch-0.0.0.tgz", + "integrity": "sha512-Pvi4hlAXWHEIT+4XlQEPPIQ02hRzvn38K/cnZ5sZeM11FsDPoXvBD6i/zyVxFK6cgqSlS8sA5/sIwUGp9+ZMhw==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "tailwind-merge": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", + "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==" + }, + "tailwindcss": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, + "tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "requires": {} + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "requires": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + } + }, + "tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" + }, + "trim-repeated": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", + "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", + "dev": true, + "requires": { + "escape-string-regexp": "^5.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true + } + } + }, + "trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "ts-morph": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-18.0.0.tgz", + "integrity": "sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==", + "requires": { + "@ts-morph/common": "~0.19.0", + "code-block-writer": "^12.0.0" + } + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "devOptional": true + }, + "unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "requires": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + } + } + }, + "unist-util-find-after": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz", + "integrity": "sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, + "unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==" + }, + "unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + } + }, + "unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, + "universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "requires": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-callback-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", + "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "requires": { + "tslib": "^2.0.0" + } + }, + "use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "requires": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + } + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + }, + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, + "vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + } + }, + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + }, + "vite": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "requires": { + "esbuild": "^0.18.10", + "fsevents": "~2.3.2", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "dependencies": { + "@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "optional": true + }, + "esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "requires": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + } + } + }, + "vite-plugin-svgr": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.3.0.tgz", + "integrity": "sha512-vWZMCcGNdPqgziYFKQ3Y95XP0d0YGp28+MM3Dp9cTa/px5CKcHHrIoPl2Jw81rgVm6/ZUNONzjXbZQZ7Kw66og==", + "requires": { + "@rollup/pluginutils": "^5.0.4", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + } + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "requires": { + "defaults": "^1.0.3" + } + }, + "web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==" + }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" + }, + "web-vitals": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", + "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==" + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xmldom-sre": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom-sre/-/xmldom-sre-0.1.31.tgz", + "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "zod": { + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.2.tgz", + "integrity": "sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==" + }, + "zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "requires": { + "use-sync-external-store": "1.2.0" + } + }, + "zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" + } } } From 8b271527128f5a3a614b36844c74452c98142687 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 10:55:54 -0300 Subject: [PATCH 211/213] =?UTF-8?q?=F0=9F=93=9D=20docs(api.mdx):=20add=20d?= =?UTF-8?q?ocumentation=20for=20API=20Keys=20in=20Langflow=20=F0=9F=94=80?= =?UTF-8?q?=20chore(sidebars.js):=20uncomment=20API=20guidelines=20in=20th?= =?UTF-8?q?e=20sidebar=20=F0=9F=96=BC=EF=B8=8F=20chore(api-key.png):=20add?= =?UTF-8?q?=20image=20for=20API=20Key=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/guidelines/api.mdx | 147 +++++++++++++++++++++++++++++++++++ docs/sidebars.js | 2 +- docs/static/img/api-key.png | Bin 0 -> 2947 bytes 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 docs/docs/guidelines/api.mdx create mode 100644 docs/static/img/api-key.png diff --git a/docs/docs/guidelines/api.mdx b/docs/docs/guidelines/api.mdx new file mode 100644 index 000000000..97d2db76e --- /dev/null +++ b/docs/docs/guidelines/api.mdx @@ -0,0 +1,147 @@ +import useBaseUrl from "@docusaurus/useBaseUrl"; +import ZoomableImage from "/src/theme/ZoomableImage.js"; + +# API Keys + +## Introduction + +Langflow offers an API Key functionality that allows users to access their individual components and flows without going through traditional login authentication. The API Key is a user-specific token that can be included in the request's header or query parameter to authenticate API calls. The following documentation outlines how to generate, use, and manage these API Keys in Langflow. + +## Generating an API Key + +### Through Langflow UI + +{/* add image img/api-key.png */} + + + +1. Click on the "API Key" icon. +2. Click on "Create new secret key". +3. Give it an optional name. +4. Click on "Create secret key". +5. Copy the API key and store it in a secure location. + +## Using the API Key + +### Using the `x-api-key` Header + +Include the `x-api-key` in the HTTP header when making API requests: + +```bash +curl -X POST \ + http://localhost:3000/api/v1/process/ \ + -H 'Content-Type: application/json'\ + -H 'x-api-key: '\ + -d '{"inputs": {"text":""}, "tweaks": {}}' +``` + +With Python using `requests`: + +```python +import requests +from typing import Optional + +BASE_API_URL = "http://localhost:3001/api/v1/process" +FLOW_ID = "4441b773-0724-434e-9cee-19d995d8f2df" +# You can tweak the flow by adding a tweaks dictionary +# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}} +TWEAKS = {} + +def run_flow(inputs: dict, + flow_id: str, + tweaks: Optional[dict] = None, + apiKey: Optional[str] = None) -> dict: + """ + Run a flow with a given message and optional tweaks. + + :param message: The message to send to the flow + :param flow_id: The ID of the flow to run + :param tweaks: Optional tweaks to customize the flow + :return: The JSON response from the flow + """ + api_url = f"{BASE_API_URL}/{flow_id}" + + payload = {"inputs": inputs} + headers = {} + + if tweaks: + payload["tweaks"] = tweaks + if apiKey: + headers = {"x-api-key": apiKey} + + response = requests.post(api_url, json=payload, headers=headers) + return response.json() + +# Setup any tweaks you want to apply to the flow +inputs = {"text":""} +api_key = "" +print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS, apiKey=api_key)) +``` + +### Using the Query Parameter + +Alternatively, you can include the API key as a query parameter in the URL: + +```bash +curl -X POST \ + http://localhost:3000/api/v1/process/?x-api-key= \ + -H 'Content-Type: application/json'\ + -d '{"inputs": {"text":""}, "tweaks": {}}' +``` + +Or with Python: + +```python +import requests + +BASE_API_URL = "http://localhost:3001/api/v1/process" +FLOW_ID = "4441b773-0724-434e-9cee-19d995d8f2df" +# You can tweak the flow by adding a tweaks dictionary +# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}} +TWEAKS = {} + +def run_flow(inputs: dict, + flow_id: str, + tweaks: Optional[dict] = None, + apiKey: Optional[str] = None) -> dict: + """ + Run a flow with a given message and optional tweaks. + + :param message: The message to send to the flow + :param flow_id: The ID of the flow to run + :param tweaks: Optional tweaks to customize the flow + :return: The JSON response from the flow + """ + api_url = f"{BASE_API_URL}/{flow_id}" + + payload = {"inputs": inputs} + headers = {} + + if tweaks: + payload["tweaks"] = tweaks + if apiKey: + api_url += f"?x-api-key={apiKey}" + + response = requests.post(api_url, json=payload, headers=headers) + return response.json() + +# Setup any tweaks you want to apply to the flow +inputs = {"text":""} +api_key = "" +print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS, apiKey=api_key)) +``` + +## Security Considerations + +- **Visibility**: The API key won't be retrievable again through the UI for security reasons. +- **Scope**: The key only allows access to the flows and components of the specific user to whom it was issued. + +## Revoking an API Key + +To revoke an API key, simply delete it from the UI. This will immediately invalidate the key and prevent it from being used again. diff --git a/docs/sidebars.js b/docs/sidebars.js index 166d72f22..116e54616 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -17,7 +17,7 @@ module.exports = { collapsed: false, items: [ "guidelines/login", - // "guidelines/api", + "guidelines/api", "guidelines/components", "guidelines/features", "guidelines/collection", diff --git a/docs/static/img/api-key.png b/docs/static/img/api-key.png new file mode 100644 index 0000000000000000000000000000000000000000..eb1c8de37171df0561fa5a5625ce6c18c8189d2d GIT binary patch literal 2947 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 literal 0 HcmV?d00001 From 12a5dd8fdeab09b161ef27d62f92d9253988ea13 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 11:13:48 -0300 Subject: [PATCH 212/213] =?UTF-8?q?=F0=9F=90=9B=20fix(utils.py):=20handle?= =?UTF-8?q?=20cases=20where=20file.filename=20is=20not=20a=20string=20or?= =?UTF-8?q?=20Path=20object=20to=20prevent=20errors=20=F0=9F=94=A7=20chore?= =?UTF-8?q?(utils.py):=20refactor=20save=5Fuploaded=5Ffile=20function=20to?= =?UTF-8?q?=20improve=20code=20readability=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/cache/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/services/cache/utils.py b/src/backend/langflow/services/cache/utils.py index 75380b3d5..83cde2240 100644 --- a/src/backend/langflow/services/cache/utils.py +++ b/src/backend/langflow/services/cache/utils.py @@ -169,7 +169,11 @@ def save_uploaded_file(file: UploadFile, folder_name): """ cache_path = Path(CACHE_DIR) folder_path = cache_path / folder_name - file_extension = Path(file.filename).suffix + filename = file.filename + if isinstance(filename, str) or isinstance(filename, Path): + file_extension = Path(filename).suffix + else: + file_extension = "" file_object = file.file # Create the folder if it doesn't exist From 305838b3dae5211de0164319a7fa7edff0b3033d Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 26 Sep 2023 11:19:45 -0300 Subject: [PATCH 213/213] Update lock --- poetry.lock | 348 +++++++++++++++++++++++++--------------------------- 1 file changed, 167 insertions(+), 181 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8fb013726..4a2bf4fb2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -135,15 +135,18 @@ frozenlist = ">=1.1.0" [[package]] name = "aiostream" -version = "0.4.5" +version = "0.5.0" description = "Generator-based operators for asynchronous iteration" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "aiostream-0.4.5-py3-none-any.whl", hash = "sha256:25b7c2d9c83570d78c0ef5a20e949b7d0b8ea3b0b0a4f22c49d3f721105a6057"}, - {file = "aiostream-0.4.5.tar.gz", hash = "sha256:3ecbf87085230fbcd9605c32ca20c4fb41af02c71d076eab246ea22e35947d88"}, + {file = "aiostream-0.5.0-py3-none-any.whl", hash = "sha256:daf1979a683452fbcd9b1a6933b3d7c9237d3c240b9737f176ab6e1aaf2d8bca"}, + {file = "aiostream-0.5.0.tar.gz", hash = "sha256:d64aa63cb9b96b4dae74b09f260d2d0fa423fbbd17bce29ed39ff8e5caf95b57"}, ] +[package.dependencies] +typing-extensions = "*" + [[package]] name = "alembic" version = "1.12.0" @@ -1429,63 +1432,47 @@ files = [ [[package]] name = "duckdb" -version = "0.8.1" +version = "0.9.0" description = "DuckDB embedded database" optional = false -python-versions = "*" +python-versions = ">=3.7.0" files = [ - {file = "duckdb-0.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:14781d21580ee72aba1f5dcae7734674c9b6c078dd60470a08b2b420d15b996d"}, - {file = "duckdb-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f13bf7ab0e56ddd2014ef762ae4ee5ea4df5a69545ce1191b8d7df8118ba3167"}, - {file = "duckdb-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4032042d8363e55365bbca3faafc6dc336ed2aad088f10ae1a534ebc5bcc181"}, - {file = "duckdb-0.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a71bd8f0b0ca77c27fa89b99349ef22599ffefe1e7684ae2e1aa2904a08684"}, - {file = "duckdb-0.8.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24568d6e48f3dbbf4a933109e323507a46b9399ed24c5d4388c4987ddc694fd0"}, - {file = "duckdb-0.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297226c0dadaa07f7c5ae7cbdb9adba9567db7b16693dbd1b406b739ce0d7924"}, - {file = "duckdb-0.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5792cf777ece2c0591194006b4d3e531f720186102492872cb32ddb9363919cf"}, - {file = "duckdb-0.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:12803f9f41582b68921d6b21f95ba7a51e1d8f36832b7d8006186f58c3d1b344"}, - {file = "duckdb-0.8.1-cp310-cp310-win32.whl", hash = "sha256:d0953d5a2355ddc49095e7aef1392b7f59c5be5cec8cdc98b9d9dc1f01e7ce2b"}, - {file = "duckdb-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e6583c98a7d6637e83bcadfbd86e1f183917ea539f23b6b41178f32f813a5eb"}, - {file = "duckdb-0.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fad7ed0d4415f633d955ac24717fa13a500012b600751d4edb050b75fb940c25"}, - {file = "duckdb-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81ae602f34d38d9c48dd60f94b89f28df3ef346830978441b83c5b4eae131d08"}, - {file = "duckdb-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d75cfe563aaa058d3b4ccaaa371c6271e00e3070df5de72361fd161b2fe6780"}, - {file = "duckdb-0.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbb55e7a3336f2462e5e916fc128c47fe1c03b6208d6bd413ac11ed95132aa0"}, - {file = "duckdb-0.8.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6df53efd63b6fdf04657385a791a4e3c4fb94bfd5db181c4843e2c46b04fef5"}, - {file = "duckdb-0.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b188b80b70d1159b17c9baaf541c1799c1ce8b2af4add179a9eed8e2616be96"}, - {file = "duckdb-0.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5ad481ee353f31250b45d64b4a104e53b21415577943aa8f84d0af266dc9af85"}, - {file = "duckdb-0.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1d1b1729993611b1892509d21c21628917625cdbe824a61ce891baadf684b32"}, - {file = "duckdb-0.8.1-cp311-cp311-win32.whl", hash = "sha256:2d8f9cc301e8455a4f89aa1088b8a2d628f0c1f158d4cf9bc78971ed88d82eea"}, - {file = "duckdb-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:07457a43605223f62d93d2a5a66b3f97731f79bbbe81fdd5b79954306122f612"}, - {file = "duckdb-0.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2c8062c3e978dbcd80d712ca3e307de8a06bd4f343aa457d7dd7294692a3842"}, - {file = "duckdb-0.8.1-cp36-cp36m-win32.whl", hash = "sha256:fad486c65ae944eae2de0d590a0a4fb91a9893df98411d66cab03359f9cba39b"}, - {file = "duckdb-0.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:86fa4506622c52d2df93089c8e7075f1c4d0ba56f4bf27faebde8725355edf32"}, - {file = "duckdb-0.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:60e07a62782f88420046e30cc0e3de842d0901c4fd5b8e4d28b73826ec0c3f5e"}, - {file = "duckdb-0.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18563675977f8cbf03748efee0165b4c8ef64e0cbe48366f78e2914d82138bb"}, - {file = "duckdb-0.8.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16e179443832bea8439ae4dff93cf1e42c545144ead7a4ef5f473e373eea925a"}, - {file = "duckdb-0.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a413d5267cb41a1afe69d30dd6d4842c588256a6fed7554c7e07dad251ede095"}, - {file = "duckdb-0.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3784680df59eadd683b0a4c2375d451a64470ca54bd171c01e36951962b1d332"}, - {file = "duckdb-0.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:67a1725c2b01f9b53571ecf3f92959b652f60156c1c48fb35798302e39b3c1a2"}, - {file = "duckdb-0.8.1-cp37-cp37m-win32.whl", hash = "sha256:197d37e2588c5ad063e79819054eedb7550d43bf1a557d03ba8f8f67f71acc42"}, - {file = "duckdb-0.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3843feb79edf100800f5037c32d5d5a5474fb94b32ace66c707b96605e7c16b2"}, - {file = "duckdb-0.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:624c889b0f2d656794757b3cc4fc58030d5e285f5ad2ef9fba1ea34a01dab7fb"}, - {file = "duckdb-0.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fcbe3742d77eb5add2d617d487266d825e663270ef90253366137a47eaab9448"}, - {file = "duckdb-0.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47516c9299d09e9dbba097b9fb339b389313c4941da5c54109df01df0f05e78c"}, - {file = "duckdb-0.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf1ba718b7522d34399446ebd5d4b9fcac0b56b6ac07bfebf618fd190ec37c1d"}, - {file = "duckdb-0.8.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e36e35d38a9ae798fe8cf6a839e81494d5b634af89f4ec9483f4d0a313fc6bdb"}, - {file = "duckdb-0.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23493313f88ce6e708a512daacad13e83e6d1ea0be204b175df1348f7fc78671"}, - {file = "duckdb-0.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1fb9bf0b6f63616c8a4b9a6a32789045e98c108df100e6bac783dc1e36073737"}, - {file = "duckdb-0.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12fc13ecd5eddd28b203b9e3999040d3a7374a8f4b833b04bd26b8c5685c2635"}, - {file = "duckdb-0.8.1-cp38-cp38-win32.whl", hash = "sha256:a12bf4b18306c9cb2c9ba50520317e6cf2de861f121d6f0678505fa83468c627"}, - {file = "duckdb-0.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e4e809358b9559c00caac4233e0e2014f3f55cd753a31c4bcbbd1b55ad0d35e4"}, - {file = "duckdb-0.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7acedfc00d97fbdb8c3d120418c41ef3cb86ef59367f3a9a30dff24470d38680"}, - {file = "duckdb-0.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:99bfe264059cdc1e318769103f656f98e819cd4e231cd76c1d1a0327f3e5cef8"}, - {file = "duckdb-0.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:538b225f361066231bc6cd66c04a5561de3eea56115a5dd773e99e5d47eb1b89"}, - {file = "duckdb-0.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae0be3f71a18cd8492d05d0fc1bc67d01d5a9457b04822d025b0fc8ee6efe32e"}, - {file = "duckdb-0.8.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd82ba63b58672e46c8ec60bc9946aa4dd7b77f21c1ba09633d8847ad9eb0d7b"}, - {file = "duckdb-0.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:780a34559aaec8354e83aa4b7b31b3555f1b2cf75728bf5ce11b89a950f5cdd9"}, - {file = "duckdb-0.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:01f0d4e9f7103523672bda8d3f77f440b3e0155dd3b2f24997bc0c77f8deb460"}, - {file = "duckdb-0.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31f692decb98c2d57891da27180201d9e93bb470a3051fcf413e8da65bca37a5"}, - {file = "duckdb-0.8.1-cp39-cp39-win32.whl", hash = "sha256:e7fe93449cd309bbc67d1bf6f6392a6118e94a9a4479ab8a80518742e855370a"}, - {file = "duckdb-0.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:81d670bc6807672f038332d9bf587037aabdd741b0810de191984325ed307abd"}, - {file = "duckdb-0.8.1.tar.gz", hash = "sha256:a54d37f4abc2afc4f92314aaa56ecf215a411f40af4bffe1e86bd25e62aceee9"}, + {file = "duckdb-0.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ffb28663810679c77c7a4d6e799a45991b13af413fc9e012e65221e48ef58a8d"}, + {file = "duckdb-0.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8770917c52007bd4873aa81a9fab13e015be45cabd2f32092e4cd04bbd043da7"}, + {file = "duckdb-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d26ac0d59e1e1c9c0faaafa94487016abde4f431b45e3da7a4c37f2ffbcecae"}, + {file = "duckdb-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f6d982cf28d8257dedf371c85d00523114f600018f4e013b01d2072f8f7b9"}, + {file = "duckdb-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c144c18da348af5201d9526556d3f3bddbf4882a71106a4ced9acca0ea579938"}, + {file = "duckdb-0.9.0-cp310-cp310-win32.whl", hash = "sha256:ed4fff7a46a0fb3780bde1dc9ce07f2e2947f625e3f26d41771b5d54150f0a3f"}, + {file = "duckdb-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:577e4d5d356634ebd4a02a5e0a1c8e980d6e5dc032f958cc310f68686840745d"}, + {file = "duckdb-0.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8900a9b2dc5eff4849025755801be156cfb16326ff8d0bc5f3787321f1553be"}, + {file = "duckdb-0.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2bad2145350a81f32852e09f00cea43533cce2dd41e974792f60ce24ecdae30"}, + {file = "duckdb-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:139c0af678dd7ca4a37c744db67a32dd65ee62424dcb09f4ac6c839115ec67a4"}, + {file = "duckdb-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15dc13cac315741c7fa97907ed8b999b31c10fb3347a142d367c8ce0ed8be63e"}, + {file = "duckdb-0.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d5d8b3e6cad93ab9ac92ce1a2db7ea4836cc2a310efc8e4d8d79648050e2804"}, + {file = "duckdb-0.9.0-cp311-cp311-win32.whl", hash = "sha256:c3b677cade05bf99a225c164ca46384d0182e7d09b47998174df5b4711717a62"}, + {file = "duckdb-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ed27ef0f1bc8b4f64bed124331d474b0f3106de91fd086e00fa98c670df488"}, + {file = "duckdb-0.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1407e2e75c10cd9988bae7ad0450208223660bc821d56e06114d5cb8f5358580"}, + {file = "duckdb-0.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2388e9dcc38c08efef8d74e5f63db691329dc9ed5c68b7a6619aae9678f10ebc"}, + {file = "duckdb-0.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:347b7aa85aef8cfd0a53143aded0e121aed51cf74713b264ade22fedd3af2bd8"}, + {file = "duckdb-0.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e4636a4d14590719bf67c317b3df04ef3269f68829340457ce0c47e8eb759d39"}, + {file = "duckdb-0.9.0-cp37-cp37m-win32.whl", hash = "sha256:745138da6645286e098dff3be55fbd64eba8d13ba3cfb823fb0d5c0e4d5f646c"}, + {file = "duckdb-0.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ac10228540cacbbb9483842d429212c7e61fc02bd94d07ab6eee69beb4e1f5d2"}, + {file = "duckdb-0.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0595110431d7f53663ed2b9d25ca393feefa3a096fbaf33af8f500d7d7a40bd9"}, + {file = "duckdb-0.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ab4a6bc961944ad8b23a477c322b9be5f3846ef52a6ff096a0076989a4a841a1"}, + {file = "duckdb-0.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf33ea1a3882d83d744282e2b0db79fe274d64ebc2beff31e42d13f5d2093f22"}, + {file = "duckdb-0.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af5cf448bca9b5acb8a5d2a451739fc5545bca3b5222db73a8faade6ff499be"}, + {file = "duckdb-0.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424fd1e3dde074660865557f2138b2f988b9e499bc1eaaf391a1912d19e909dd"}, + {file = "duckdb-0.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:72cfa8e1de0b29719fa81c7afe4bb0b3011cb4d9c5c2ecf18dce14912d6a4386"}, + {file = "duckdb-0.9.0-cp38-cp38-win32.whl", hash = "sha256:36cb92ccf75d7c18d533ab959d5e9febf1d0977647eba8b85f54d60edd4ceafd"}, + {file = "duckdb-0.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:4134aad5ed0399d621180a2a09d96b7dd94b18c01682da14e41930ef16e50c78"}, + {file = "duckdb-0.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a0082b96c4141f87dc6440e76ef13e39f191a131534ffafadd6cf279766f1ecd"}, + {file = "duckdb-0.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4302fb0be33cdbff9404656be0b45bba6e549a67180f03c262bfcb1e41288db"}, + {file = "duckdb-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a115a430c607a3287f4af26d18820a9eab56925703142b727bc3f7204b50aa3f"}, + {file = "duckdb-0.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5615e7854f042f1c7a5c4c3959de64861677b63029b08ed72c94abffcdc70483"}, + {file = "duckdb-0.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8784986a04bac9873fa538775c1fb5b677baad90ddc9371f7b4a89468b1c1ab6"}, + {file = "duckdb-0.9.0-cp39-cp39-win32.whl", hash = "sha256:584b1b63d74076c3dd4188582c2f59ac98177fe751e1fa34d75345e166e70d73"}, + {file = "duckdb-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:40814f12c4c1ec222c8df05763528dbbffd175c8bcc5e3c45c3950c6a9b3f209"}, + {file = "duckdb-0.9.0.tar.gz", hash = "sha256:3a52c975cc13b965580cd00af1538b2d9f6b896431f97121dbee7ce715edcd2a"}, ] [[package]] @@ -1847,13 +1834,13 @@ files = [ [[package]] name = "fsspec" -version = "2023.9.1" +version = "2023.9.2" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2023.9.1-py3-none-any.whl", hash = "sha256:99a974063b6cced36cfaa61aa8efb05439c6fea2dafe65930e7ab46f9d2f8930"}, - {file = "fsspec-2023.9.1.tar.gz", hash = "sha256:da8cfe39eeb65aaa69074d5e0e4bbc9b7ef72d69c0587a31cab981eefdb3da13"}, + {file = "fsspec-2023.9.2-py3-none-any.whl", hash = "sha256:603dbc52c75b84da501b9b2ec8c11e1f61c25984c4a0dda1f129ef391fbfc9b4"}, + {file = "fsspec-2023.9.2.tar.gz", hash = "sha256:80bfb8c70cc27b2178cc62a935ecf242fc6e8c3fb801f9c571fc01b1e715ba7d"}, ] [package.extras] @@ -2098,13 +2085,13 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit" [[package]] name = "google-api-core" -version = "2.11.1" +version = "2.12.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.11.1.tar.gz", hash = "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a"}, - {file = "google_api_core-2.11.1-py3-none-any.whl", hash = "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a"}, + {file = "google-api-core-2.12.0.tar.gz", hash = "sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553"}, + {file = "google_api_core-2.12.0-py3-none-any.whl", hash = "sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160"}, ] [package.dependencies] @@ -2122,13 +2109,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.100.0" +version = "2.101.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-python-client-2.100.0.tar.gz", hash = "sha256:eaed50efc2f8a4027dcca8fd0037f4b1b03b8093efc84ce3cb6c75bfc79a7e31"}, - {file = "google_api_python_client-2.100.0-py2.py3-none-any.whl", hash = "sha256:226ca35355993d6182506c51745ab5149405cdf6a92975b2725ab3e0d757dbe9"}, + {file = "google-api-python-client-2.101.0.tar.gz", hash = "sha256:e9620a809251174818e1fce16604006f10a9c2ac0d3d94a139cdddcd4dbea2d8"}, + {file = "google_api_python_client-2.101.0-py2.py3-none-any.whl", hash = "sha256:71760dcf11d191b65520d1c13757a776f4f43cf87f302097a0d8e491c2ef87b0"}, ] [package.dependencies] @@ -3529,13 +3516,13 @@ test = ["psutil", "pytest", "pytest-asyncio"] [[package]] name = "langfuse" -version = "1.0.24" +version = "1.0.25" description = "A client library for accessing langfuse" optional = false python-versions = ">=3.8.1,<4.0" files = [ - {file = "langfuse-1.0.24-py3-none-any.whl", hash = "sha256:b24c8410f77a3a418c441e560f0c5609df3ad0ed7be38c186e6db1956301d51a"}, - {file = "langfuse-1.0.24.tar.gz", hash = "sha256:f11c448d1e32f1b4e25df5eaf902cb104f74aa6a3a7f91267504121dc57fcb82"}, + {file = "langfuse-1.0.25-py3-none-any.whl", hash = "sha256:7b724eba534bacfb5e7225ba9ed2be3701f893a26a8be171cb9ad8b041139df5"}, + {file = "langfuse-1.0.25.tar.gz", hash = "sha256:4a6ee9958e51f86ffa056f475e03413b2befdde366c4523007ec85863bb91c70"}, ] [package.dependencies] @@ -3960,13 +3947,13 @@ files = [ [[package]] name = "metal-sdk" -version = "2.1.3" +version = "2.1.4" description = "SDK for getmetal.io" optional = false python-versions = ">=3.7" files = [ - {file = "metal_sdk-2.1.3-py3-none-any.whl", hash = "sha256:c79f4ddb78b9ec83bb73969ce535ae33c601d34525088ee40268bc2ac5f69ba2"}, - {file = "metal_sdk-2.1.3.tar.gz", hash = "sha256:43cb6244d7af5e1fe21b3d0fe59ee2e0f40d0350ea4779e80d9cf366d760407e"}, + {file = "metal_sdk-2.1.4-py3-none-any.whl", hash = "sha256:b6f23b5aa5382d8a49eb20bc95992c2c796f4d3056dbcda5259afd86f301d853"}, + {file = "metal_sdk-2.1.4.tar.gz", hash = "sha256:9d6060ab1e9e8605547281396b3254567ffe6ae56a6855ede1dd6fe1de1326a2"}, ] [package.dependencies] @@ -4340,41 +4327,40 @@ twitter = ["twython"] [[package]] name = "numexpr" -version = "2.8.6" +version = "2.8.7" description = "Fast numerical expression evaluator for NumPy" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "numexpr-2.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80acbfefb68bd92e708e09f0a02b29e04d388b9ae72f9fcd57988aca172a7833"}, - {file = "numexpr-2.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e884687da8af5955dc9beb6a12d469675c90b8fb38b6c93668c989cfc2cd982"}, - {file = "numexpr-2.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ef7e8aaa84fce3aba2e65f243d14a9f8cc92aafd5d90d67283815febfe43eeb"}, - {file = "numexpr-2.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee04d72307c09599f786b9231acffb10df7d7a74b2ce3681d74a574880d13ce"}, - {file = "numexpr-2.8.6-cp310-cp310-win32.whl", hash = "sha256:211804ec25a9f6d188eadf4198dd1a92b2f61d7d20993c6c7706139bc4199c5b"}, - {file = "numexpr-2.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:18b1804923cfa3be7bbb45187d01c0540c8f6df4928c22a0f786e15568e9ebc5"}, - {file = "numexpr-2.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95b9da613761e4fc79748535b2a1f58cada22500e22713ae7d9571fa88d1c2e2"}, - {file = "numexpr-2.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47b45da5aa25600081a649f5e8b2aa640e35db3703f4631f34bb1f2f86d1b5b4"}, - {file = "numexpr-2.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84979bf14143351c2db8d9dd7fef8aca027c66ad9df9cb5e75c93bf5f7b5a338"}, - {file = "numexpr-2.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d36528a33aa9c23743b3ea686e57526a4f71e7128a1be66210e1511b09c4e4e9"}, - {file = "numexpr-2.8.6-cp311-cp311-win32.whl", hash = "sha256:681812e2e71ff1ba9145fac42d03f51ddf6ba911259aa83041323f68e7458002"}, - {file = "numexpr-2.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:27782177a0081bd0aab229be5d37674e7f0ab4264ef576697323dd047432a4cd"}, - {file = "numexpr-2.8.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ef6e8896457a60a539cb6ba27da78315a9bb31edb246829b25b5b0304bfcee91"}, - {file = "numexpr-2.8.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e640bc0eaf1b59f3dde52bc02bbfda98e62f9950202b0584deba28baf9f36bbb"}, - {file = "numexpr-2.8.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d126938c2c3784673c9c58d94e00b1570aa65517d9c33662234d442fc9fb5795"}, - {file = "numexpr-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:e93d64cd20940b726477c3cb64926e683d31b778a1e18f9079a5088fd0d8e7c8"}, - {file = "numexpr-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:31cf610c952eec57081171f0b4427f9bed2395ec70ec432bbf45d260c5c0cdeb"}, - {file = "numexpr-2.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5f96c89aa0b1f13685ec32fa3d71028db0b5981bfd99a0bbc271035949136b3"}, - {file = "numexpr-2.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c8f37f7a6af3bdd61f2efd1cafcc083a9525ab0aaf5dc641e7ec8fc0ae2d3aa1"}, - {file = "numexpr-2.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38b8b90967026bbc36c7aa6e8ca3b8906e1990914fd21f446e2a043f4ee3bc06"}, - {file = "numexpr-2.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1967c16f61c27df1cdc43ba3c0ba30346157048dd420b4259832276144d0f64e"}, - {file = "numexpr-2.8.6-cp38-cp38-win32.whl", hash = "sha256:15469dc722b5ceb92324ec8635411355ebc702303db901ae8cc87f47c5e3a124"}, - {file = "numexpr-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:95c09e814b0d6549de98b5ded7cdf7d954d934bb6b505432ff82e83a6d330bda"}, - {file = "numexpr-2.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:aa0f661f5f4872fd7350cc9895f5d2594794b2a7e7f1961649a351724c64acc9"}, - {file = "numexpr-2.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8e3e6f1588d6c03877cb3b3dcc3096482da9d330013b886b29cb9586af5af3eb"}, - {file = "numexpr-2.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8564186aad5a2c88d597ebc79b8171b52fd33e9b085013e1ff2208f7e4b387e3"}, - {file = "numexpr-2.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6a88d71c166e86b98d34701285d23e3e89d548d9f5ae3f4b60919ac7151949f"}, - {file = "numexpr-2.8.6-cp39-cp39-win32.whl", hash = "sha256:c48221b6a85494a7be5a022899764e58259af585dff031cecab337277278cc93"}, - {file = "numexpr-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:6d7003497d82ef19458dce380b36a99343b96a3bd5773465c2d898bf8f5a38f9"}, - {file = "numexpr-2.8.6.tar.gz", hash = "sha256:6336f8dba3f456e41a4ffc3c97eb63d89c73589ff6e1707141224b930263260d"}, + {file = "numexpr-2.8.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d88531ffea3ea9287e8a1665c6a2d0206d3f4660d5244423e2a134a7f0ce5fba"}, + {file = "numexpr-2.8.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db1065ba663a854115cf1f493afd7206e2efcef6643129e8061e97a51ad66ebb"}, + {file = "numexpr-2.8.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4546416004ff2e7eb9cf52c2d7ab82732b1b505593193ee9f93fa770edc5230"}, + {file = "numexpr-2.8.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb2f473fdfd09d17db3038e34818d05b6bc561a36785aa927d6c0e06bccc9911"}, + {file = "numexpr-2.8.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5496fc9e3ae214637cbca1ab556b0e602bd3afe9ff4c943a29c482430972cda8"}, + {file = "numexpr-2.8.7-cp310-cp310-win32.whl", hash = "sha256:d43f1f0253a6f2db2f76214e6f7ae9611b422cba3f7d4c86415d7a78bbbd606f"}, + {file = "numexpr-2.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:cf5f112bce5c5966c47cc33700bc14ce745c8351d437ed57a9574fff581f341a"}, + {file = "numexpr-2.8.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:32934d51b5bc8a6636436326da79ed380e2f151989968789cf65b1210572cb46"}, + {file = "numexpr-2.8.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f021ac93cb3dd5d8ba2882627b615b1f58cb089dcc85764c6fbe7a549ed21b0c"}, + {file = "numexpr-2.8.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dccf572763517db6562fb7b17db46aacbbf62a9ca0a66672872f4f71aee7b186"}, + {file = "numexpr-2.8.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11121b14ee3179bade92e823f25f1b94e18716d33845db5081973331188c3338"}, + {file = "numexpr-2.8.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81451962d4145a46dba189df65df101d4d1caddb6efe6ebfe05982cd9f62b2cf"}, + {file = "numexpr-2.8.7-cp311-cp311-win32.whl", hash = "sha256:da55ba845b847cc33c4bf81cee4b1bddfb0831118cabff8db62888ab8697ec34"}, + {file = "numexpr-2.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:fd93b88d5332069916fa00829ea1b972b7e73abcb1081eee5c905a514b8b59e3"}, + {file = "numexpr-2.8.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5340d2c86d83f52e1a3e7fd97c37d358ae99af9de316bdeeab2565b9b1e622ca"}, + {file = "numexpr-2.8.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3bdf8cbc00c77a46230c765d242f92d35905c239b20c256c48dbac91e49f253"}, + {file = "numexpr-2.8.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d46c47e361fa60966a3339cb4f463ae6151ce7d78ed38075f06e8585d2c8929f"}, + {file = "numexpr-2.8.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a371cfc1670a18eea2d5c70abaa95a0e8824b70d28da884bad11931266e3a0ca"}, + {file = "numexpr-2.8.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:47a249cecd1382d482a5bf1fac0d11392fb2ed0f7d415ebc4cd901959deb1ec9"}, + {file = "numexpr-2.8.7-cp312-cp312-win32.whl", hash = "sha256:b8a5b2c21c26b62875bf819d375d798b96a32644e3c28bd4ce7789ed1fb489da"}, + {file = "numexpr-2.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:f29f4d08d9b0ed6fa5d32082971294b2f9131b8577c2b7c36432ed670924313f"}, + {file = "numexpr-2.8.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ecaa5be24cf8fa0f00108e9dfa1021b7510e9dd9d159b8d8bc7c7ddbb995b31"}, + {file = "numexpr-2.8.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a84284e0a407ca52980fd20962e89aff671c84cd6e73458f2e29ea2aa206356"}, + {file = "numexpr-2.8.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e838289e3b7bbe100b99e35496e6cc4cc0541c2207078941ee5a1d46e6b925ae"}, + {file = "numexpr-2.8.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0983052f308ea75dd232eb7f4729eed839db8fe8d82289940342b32cc55b15d0"}, + {file = "numexpr-2.8.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bf005acd7f1985c71b1b247aaac8950d6ea05a0fe0bbbbf3f96cd398b136daa"}, + {file = "numexpr-2.8.7-cp39-cp39-win32.whl", hash = "sha256:56ec95f8d1db0819e64987dcf1789acd500fa4ea396eeabe4af6efdcb8902d07"}, + {file = "numexpr-2.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:c7bf60fc1a9c90a9cb21c4c235723e579bff70c8d5362228cb2cf34426104ba2"}, + {file = "numexpr-2.8.7.tar.gz", hash = "sha256:596eeb3bbfebc912f4b6eaaf842b61ba722cebdb8bc42dfefa657d3a74953849"}, ] [package.dependencies] @@ -5284,13 +5270,13 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "psycopg" -version = "3.1.10" +version = "3.1.11" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-3.1.10-py3-none-any.whl", hash = "sha256:8bbeddae5075c7890b2fa3e3553440376d3c5e28418335dee3c3656b06fa2b52"}, - {file = "psycopg-3.1.10.tar.gz", hash = "sha256:15b25741494344c24066dc2479b0f383dd1b82fa5e75612fa4fa5bb30726e9b6"}, + {file = "psycopg-3.1.11-py3-none-any.whl", hash = "sha256:7542c45810ea16356e5126c9b4291cbc3802aa326fcbba09ff154fe380de29be"}, + {file = "psycopg-3.1.11.tar.gz", hash = "sha256:cd711edb64b07d7f8a233c365806caf7e55bbe7cbbd8d5c680f672bb5353c8d5"}, ] [package.dependencies] @@ -5298,74 +5284,74 @@ typing-extensions = ">=4.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.1.10)"] -c = ["psycopg-c (==3.1.10)"] +binary = ["psycopg-binary (==3.1.11)"] +c = ["psycopg-c (==3.1.11)"] dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] -test = ["anyio (>=3.6.2)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] [[package]] name = "psycopg-binary" -version = "3.1.10" +version = "3.1.11" description = "PostgreSQL database adapter for Python -- C optimisation distribution" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg_binary-3.1.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a529c203f6e0f4c67ba27cf8f9739eb3bc880ad70d6ad6c0e56c2230a66b5a09"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd6e14d1aeb12754a43446c77a5ce819b68875cc25ae6538089ef90d7f6dd6f7"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1583ced5948cf88124212c4503dfe5b01ac3e2dd1a2833c083917f4c4aabe8b4"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2098721c486478987be700723b28ec7a48f134eba339de36af0e745f37dfe461"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e61f7b412fca7b15dd043a0b22fd528d2ed8276e76b3764c3889e29fa65082b"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0f33e33a072e3d5af51ee4d4a439e10dbe623fe87ef295d5d688180d529f13f"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f6f7738c59262d8d19154164d99c881ed58ed377fb6f1d685eb0dc43bbcd8022"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:511d38b1e1961d179d47d5103ba9634ecfc7ead431d19a9337ef82f3a2bca807"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:666e7acf2ffdb5e8a58e8b0c1759facdb9688c7e90ee8ca7aed675803b57404d"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:57b93c756fee5f7c7bd580c34cd5d244f7d5638f8b2cf25333f97b9b8b2ebfd1"}, - {file = "psycopg_binary-3.1.10-cp310-cp310-win_amd64.whl", hash = "sha256:a1d61b7724c7215a8ea4495a5c6b704656f4b7bb6165f4cb9989b685886ebc48"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:36fff836a7823c9d71fa7faa333c74b2b081af216cebdbb0f481dce55ee2d974"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:32caf98cb00881bfcbbbae39a15f2a4e08b79ff983f1c0f13b60a888ef6e8431"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5565a6a86fee8d74f30de89e07f399567cdf59367aeb09624eb690d524339076"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9fb0d64520b29bd80a6731476ad8e1c20348dfdee00ab098899d23247b641675"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfc05ed4e74fa8615d7cc2bd57f00f97662f4e865a731dbd43da9a527e289c8c"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5b59c8cff887757ddf438ff9489d79c5e6b717112c96f5c68e16f367ff8724e"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbaf12361136afefc5faab21a174a437e71c803b083f410e5140c7605bc66b"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ff72576061c774bcce5f5440b93e63d4c430032dd056d30f6cb1988e549dd92c"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a4e91e1a8d61c60f592a1dfcebdf55e52a29fe4fdb650c5bd5414c848e77d029"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f7187269d825e84c945be7d93dd5088a4e0b6481a4bdaba3bf7069d4ac13703d"}, - {file = "psycopg_binary-3.1.10-cp311-cp311-win_amd64.whl", hash = "sha256:ba7812a593c16d9d661844dc8dd4d81548fd1c2a0ee676f3e3d8638369f4c5e4"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88caa5859740507b3596c6c2e00ceaccee2c6ab5317bc535887801ad3cc7f3e1"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a3a7e99ba10c2e83a48d79431560e0d5ca7865f68f2bac3a462dc2b151e9926"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:848f4f4707dc73f4b4e844c92f3de795b2ddb728f75132602bda5e6ba55084fc"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:415961e839bb49cfd75cd961503fb8846c0768f247db1fa7171c1ac61d38711b"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0471869e658d0c6b8c3ed53153794739c18d7dad2dd5b8e6ff023a364c20f7df"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4290060ee0d856caa979ecf675c0e6959325f508272ccf27f64c3801c7bcbde7"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:abf04bc06c8f6a1ac3dc2106d3b79c8661352e9d8a57ca2934ffa6aae8fe600a"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:51fe70708243b83bf16710d8c11b61bd46562e6a24a6300d5434380b35911059"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b658f7f8b49fb60a1c52e3f6692f690a85bdf1ad30aafe0f3f1fd74f6958cf8"}, - {file = "psycopg_binary-3.1.10-cp37-cp37m-win_amd64.whl", hash = "sha256:ffc8c796194f23b9b07f6d25f927ec4df84a194bbc7a1f9e73316734eef512f9"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74ce92122be34cf0e5f06d79869e1001c8421a68fa7ddf6fe38a717155cf3a64"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:75608a900984061c8898be68fbddc6f3da5eefdffce6e0624f5371645740d172"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6670d160d054466e8fdedfbc749ef8bf7dfdf69296048954d24645dd4d3d3c01"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d32026cfab7ba7ac687a42c33345026a2fb6fc5608a6144077f767af4386be0b"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:908fa388a5b75dfd17a937acb24708bd272e21edefca9a495004c6f70ec2636a"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e46b97073bd4de114f475249d681eaf054e950699c5d7af554d3684db39b82d"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9cf56bb4b115def3a18157f3b3b7d8322ee94a8dea30028db602c8f9ae34ad1e"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3b6c6f90241c4c5a6ca3f0d8827e37ef90fdc4deb9d8cfa5678baa0ea374b391"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:747176a6aeb058079f56c5397bd90339581ab7b3cc0d62e7445654e6a484c7e1"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41a415e78c457b06497fa0084e4ea7245ca1a377b55756dd757034210b64da7e"}, - {file = "psycopg_binary-3.1.10-cp38-cp38-win_amd64.whl", hash = "sha256:a7bbe9017edd898d7b3a8747700ed045dda96a907dff87f45e642e28d8584481"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0f062f20256708929a58c41d44f350efced4c00a603323d1413f6dc0b84d95a5"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dea30f2704337ca2d0322fccfe1fa30f61ce9185de3937eb986321063114a51f"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9d88ac72531034ebf7ec09114e732b066a9078f4ce213cf65cc5e42eb538d30"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2bea0940d69c3e24a72530730952687912893b34c53aa39e79045e7b446174d"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a691dc8e2436d9c1e5cf93902d63e9501688fccc957eb22f952d37886257470"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa92661f99351765673835a4d936d79bd24dfbb358b29b084d83be38229a90e4"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:30eb731ed5525d8df892db6532cc8ffd8a163b73bc355127dee9c49334e16eee"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:50bf7a59d3a85a82d466fed341d352b44d09d6adc18656101d163a7cfc6509a0"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f48665947c55f8d6eb3f0be98de80411508e1ec329f354685329b57fced82c7f"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:caa771569da01fc0389ca34920c331a284425a68f92d1ba0a80cc08935f8356e"}, - {file = "psycopg_binary-3.1.10-cp39-cp39-win_amd64.whl", hash = "sha256:b30887e631fd67affaed98f6cd2135b44f2d1a6d9bca353a69c3889c78bd7aa8"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6b643d191b493251d3b2834a2929ab702b9e3539cdf2d4c5a923a6bc9aa4985"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1125455cffb7e39d937aa43c4b738367a65969ac8daf6a688a5a83b7872c2c89"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adf2a5e113ccbf5ba30133e91814bec2d5a10629bdf365b52f7d727994f51c54"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f966b201d61a9021ce0bd57ac89863e3892654e1aa8d94e9ed6fbd09931f9d04"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:67c9717599fa6ec5852634ca4e64405af4b7cbfa5ce4e96ce6a101f27a75e6f6"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7eb04da13d588c0407488ab3c003b81d8bbd20bdd38630690974b1329f6acb78"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9671379d1e6118924010b20490fa66d0b46a7868b5571ff77ee9f8d1f5f6137d"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:86c460e1d86d70fdc919109beee2e332ed5b708a99a676564657ffd49da1e330"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cb725f3b168d4d714854a62609fd85cebc9be76a94851d8ca62efc604d6ae38c"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d17d5608b08f4e5650f726a875a74c44bc840ac1d58d407f3fa58576b11f8840"}, + {file = "psycopg_binary-3.1.11-cp310-cp310-win_amd64.whl", hash = "sha256:112d59e083bee10f58636f343a5dbafd63eaf549cc00a0eb1d278d47f94244ab"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4ee614cef003e0b79ccc6b21505405aaf6b144596c8e93f08e4e5061b20382f"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9093421fcafc935402d4e1caf1f9b95879f8f8c8e50f18568b233fbe110a0cff"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:403a43f05b6cc93c749f9c60f609b3127d2fed28783df00d93ce1c34879174e4"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:264ce7152713310ffa355d4e746ffcecb5706836b086c30fff16e522b36052b5"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd16d03ef0b1cfff0b9adffa59265f37ecbf060d92466458970d6bffe68f0baa"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6f60975171ce97ccf63c953c038abd70c2f0a92b5246e231f2e23df4fe95d6"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a01c2de6a798ccbe3df0cdd608611e7988820e40b7b6e166698e2570d422ecd8"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d2bf3718620bd94486d697084d64cabe82766270cc639eee8792af4467c07de"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b731a4510ffc0fd98f852ea54397cb173ff7ec0d78e4f1e56cbb0736c58a2f2"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2dcd661f28d86d46bbafb390a36ebc095fd6aaf6851af865f136149ef0365103"}, + {file = "psycopg_binary-3.1.11-cp311-cp311-win_amd64.whl", hash = "sha256:856fa116847e02c64cb218e566d6dc282f1ae015cf6301ecfd1f184f0ca63aa3"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:549b6437c7664a8d6d151927b9b8a47a893f6fb494ca203bfcea9bfe900025bc"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41806470423ad22750871096293a61dde84b9a8bc2afe445b24a43ddaab32523"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5f427c31422452f50bc37807d863d08191356a6569ad0a81b2c46c1a86bea53"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52b2dc4fd159e5efae9b352e193427b8c418c1db4f2a243ab3f91c47c288bad5"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:992a828ef15c14b2d9a620d5308b90ca8c5002db3e67a1707a79579942e7a32e"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ba7df55a331f8db15bd38fb73531fafff2dd847956170a0ca56a431fc8cad94a"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8d59390ac47390e997d18bb51a578c66d6a40f29fd37aea67873410fd985e9f6"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:7dd68347e7ac28cd7eca5b3e56dfb9f316b5bf26c16c5e40372d9689b2a4e9f9"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6b25d734c3944e8e7e37872ce90a8de41fc6e57b547611ff6339a0dd1ad74b33"}, + {file = "psycopg_binary-3.1.11-cp37-cp37m-win_amd64.whl", hash = "sha256:5774ebecff7cf4088c301d77991de9dab52e5eba5ecd4c896afbb4f9f0846c97"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc206326bce9942e62f1fa80cc1a764ba8af0fa7dad9c7441209846e0323d5d1"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:833d088533d1d6ed758d0fd263666f0d7ceb8a4d8dd4ae440ac041fdf018f651"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89bc2fe2514035556a7fcb5afc52136767359094b937951067975879e94989eb"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef0be7c87d9ec0aa29a8bbf0399cf16d0d768331619898b28c6088960215f646"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:593a3081754e57deec56ce7ee8449837a3b937eab2cd6f52aaa59ecbf76005fc"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddd364ea43f799be615e3729210f19a58e0996dda7b01a53de0e7001f15d082"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f2b2b51d6bee271bb5ebe37beca751107276bb4f6ea32d960b44ef1cef3b571"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f4cd0c5ac86e2e1f32e93de3a124f79a7a5d24364349de7120535116775047aa"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:460aa41b4b42e640bed3127de80324a292f5a91032af7da441b6a820cb6907f9"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81a7efca200b0eba256c4141e255c1961c4ed1b099904cc57fa01f4751b43255"}, + {file = "psycopg_binary-3.1.11-cp38-cp38-win_amd64.whl", hash = "sha256:4fc2a2f89b9f985a01e2a3830d9fa7572bf902e11eb0229f8cf46c52bf8d5520"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:96c9d992d0b1fd014a0f824b49eb02b6c512608e343dbabdee0fe261a2c9496a"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7da780c259257d8cf78a1b766cfaf493a26a27cdfccd9c8d62289ae54ab663f"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c3bc72e937097b0e7d586cdee0a4d30e19651c406eb5e6d225a4bf4db3d234"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f13f27232397a24432d74a41feb87ea376fb6f8f3d28eb42c526e3403bbdea0"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6ad686b35f9c65c0c917255da72682436caf96c5f6e9fa6a0d26e1990d40ca6"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c481d3371f7f2e0cada201cdd0d30739633729a243ed6aadb54563fd73b654b0"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:12f296e0af084b463a2dd65de6987dc833e12cfab940ef3c451ef05a420322bc"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3a5b25093370280e176b2830012363b48f05fb7773ebaf3b9ab00dbf5027952c"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e9e2849fec6dd461ef0923a6b2bb659d7d7eafb5c9f9302f27a8cab205abe406"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b88e6204e9c8d6fcd41928ac3721930e404ae64dd4718fc0ff5cf4559ff11d88"}, + {file = "psycopg_binary-3.1.11-cp39-cp39-win_amd64.whl", hash = "sha256:c95a61b49ca62600eaa54165e5c7630c3bced7957402df83f511acd48ff2abf6"}, ] [[package]] @@ -5776,13 +5762,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pypdf" -version = "3.16.1" +version = "3.16.2" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.6" files = [ - {file = "pypdf-3.16.1-py3-none-any.whl", hash = "sha256:7fc9eac57162c1c4651ffae1ae96dee911d8e75af66e83b2453b2a553a8814cc"}, - {file = "pypdf-3.16.1.tar.gz", hash = "sha256:aff9540e6c5ec135d6e80943db74257523639325162d00c903ee1e2be84351fc"}, + {file = "pypdf-3.16.2-py3-none-any.whl", hash = "sha256:d132953be1e2af7b115fbfd445459fdfc601a845ca12379160e1b6afaa1fef2c"}, + {file = "pypdf-3.16.2.tar.gz", hash = "sha256:6e000281fd0f4cd32e6f1e75b05af0b6c0fbbd9777fa9a8e9d86e34cda65419d"}, ] [package.dependencies] @@ -7505,13 +7491,13 @@ telegram = ["requests"] [[package]] name = "traitlets" -version = "5.10.0" +version = "5.10.1" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.10.0-py3-none-any.whl", hash = "sha256:417745a96681fbb358e723d5346a547521f36e9bd0d50ba7ab368fff5d67aa54"}, - {file = "traitlets-5.10.0.tar.gz", hash = "sha256:f584ea209240466e66e91f3c81aa7d004ba4cf794990b0c775938a1544217cd1"}, + {file = "traitlets-5.10.1-py3-none-any.whl", hash = "sha256:07ab9c5bf8a0499fd7b088ba51be899c90ffc936ffc797d7b6907fc516bcd116"}, + {file = "traitlets-5.10.1.tar.gz", hash = "sha256:db9c4aa58139c3ba850101913915c042bdba86f7c8a0dda1c6f7f92c5da8e542"}, ] [package.extras] @@ -7868,24 +7854,24 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.11" +version = "6.0.12.12" description = "Typing stubs for PyYAML" optional = false python-versions = "*" files = [ - {file = "types-PyYAML-6.0.12.11.tar.gz", hash = "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b"}, - {file = "types_PyYAML-6.0.12.11-py3-none-any.whl", hash = "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d"}, + {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"}, + {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"}, ] [[package]] name = "types-redis" -version = "4.6.0.6" +version = "4.6.0.7" description = "Typing stubs for redis" optional = false python-versions = "*" files = [ - {file = "types-redis-4.6.0.6.tar.gz", hash = "sha256:7865a843802937ab2ddca33579c4e255bfe73f87af85824ead7a6729ba92fc52"}, - {file = "types_redis-4.6.0.6-py3-none-any.whl", hash = "sha256:e0e9dcc530623db3a41ec058ccefdcd5c7582557f02ab5f7aa9a27fe10a78d7e"}, + {file = "types-redis-4.6.0.7.tar.gz", hash = "sha256:28c4153ddb5c9d4f10def44a2454673c361d2d5fc3cd867cf3bb1520f3f59a38"}, + {file = "types_redis-4.6.0.7-py3-none-any.whl", hash = "sha256:05b1bf92879b25df20433fa1af07784a0d7928c616dc2ebf9087618db77ccbd0"}, ] [package.dependencies] @@ -7894,13 +7880,13 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.31.0.3" +version = "2.31.0.5" description = "Typing stubs for requests" optional = false python-versions = "*" files = [ - {file = "types-requests-2.31.0.3.tar.gz", hash = "sha256:d5d7a08965fca12bedf716eaf5430c6e3d0da9f3164a1dba2a7f3885f9ebe3c0"}, - {file = "types_requests-2.31.0.3-py3-none-any.whl", hash = "sha256:938f51653c757716aeca5d72c405c5e2befad8b0d330e3b385ce7f148e1b10dc"}, + {file = "types-requests-2.31.0.5.tar.gz", hash = "sha256:e4153c2a4e48dcc661600fa5f199b483cdcbd21965de0b5e2df26e93343c0f57"}, + {file = "types_requests-2.31.0.5-py3-none-any.whl", hash = "sha256:e2523825754b2832e04cdc1e731423390e731457890113a201ebca8ad9b40427"}, ] [package.dependencies]