From 7a36cc9ebf9ad320b98c003236deaeff6237bdeb Mon Sep 17 00:00:00 2001 From: William Espegren <131612909+WilliamEspegren@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:53:05 +0200 Subject: [PATCH 01/38] feat: add Spider Web Scraper & Crawler (#2439) * spider files * rebuild required * add spider-client here * Feat: Spider Web Crawler & Scraper * Feat: spider integration * new input not working * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * fix: add outputs and configure build method * style: run ruff * Refactor SpiderTool to use 'crawl' instead of 'build' for generating Markdown content * chore: add type ignore * chore: new lock * chore: Update mem0ai dependency to version 0.0.5 --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida --- poetry.lock | 143 +- pyproject.toml | 1 + .../langchain_utilities/spider_constants.py | 1 + .../components/embeddings/AstraVectorize.py | 15 +- .../langchain_utilities/SpiderTool.py | 121 + src/backend/base/langflow/main.py | 2 +- src/backend/base/poetry.lock | 2333 ++++++++++++++++- src/backend/base/pyproject.toml | 4 + src/frontend/src/icons/Spider/SpiderIcon.jsx | 18 + src/frontend/src/icons/Spider/index.tsx | 9 + src/frontend/src/icons/Spider/spider_logo.svg | 1 + src/frontend/src/utils/styleUtils.ts | 2 + 12 files changed, 2581 insertions(+), 69 deletions(-) create mode 100644 src/backend/base/langflow/base/langchain_utilities/spider_constants.py create mode 100644 src/backend/base/langflow/components/langchain_utilities/SpiderTool.py create mode 100644 src/frontend/src/icons/Spider/SpiderIcon.jsx create mode 100644 src/frontend/src/icons/Spider/index.tsx create mode 100644 src/frontend/src/icons/Spider/spider_logo.svg diff --git a/poetry.lock b/poetry.lock index 60ce75152319..8ed44e90b426 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2124,13 +2124,13 @@ idna = ">=2.0.0" [[package]] name = "embedchain" -version = "0.1.119" +version = "0.1.118" description = "Simplest open source retrieval (RAG) framework" optional = false python-versions = "<=3.13,>=3.9" files = [ - {file = "embedchain-0.1.119-py3-none-any.whl", hash = "sha256:8ec3e7f139939fa1dc8fda898f8d8d9d31a5abfe08e184b607e38733d863d606"}, - {file = "embedchain-0.1.119.tar.gz", hash = "sha256:0f4f45e092b7f3192ea6fe82575726532573b1231d7af6c22edc695b701b4223"}, + {file = "embedchain-0.1.118-py3-none-any.whl", hash = "sha256:38ead471df9d9234bf42e6f7a32cab26431d50d6f2f894f18a6cabc0b02bf31a"}, + {file = "embedchain-0.1.118.tar.gz", hash = "sha256:1fa1e799882a1dc4e63af344595b043f1c1f30fbd59461b6660b1934b85a1e4b"}, ] [package.dependencies] @@ -2144,7 +2144,7 @@ langchain = ">0.2,<=0.3" langchain-cohere = ">=0.1.4,<0.2.0" langchain-community = ">=0.2.6,<0.3.0" langchain-openai = ">=0.1.7,<0.2.0" -mem0ai = ">=0.0.9,<0.0.10" +mem0ai = ">=0.0.5,<0.0.6" openai = ">=1.1.1" posthog = ">=3.0.2,<4.0.0" pypdf = ">=4.0.1,<5.0.0" @@ -2211,6 +2211,20 @@ django = ["dj-database-url", "dj-email-url", "django-cache-url"] lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] +[[package]] +name = "eval-type-backport" +version = "0.2.0" +description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." +optional = false +python-versions = ">=3.8" +files = [ + {file = "eval_type_backport-0.2.0-py3-none-any.whl", hash = "sha256:ac2f73d30d40c5a30a80b8739a789d6bb5e49fdffa66d7912667e2015d9c9933"}, + {file = "eval_type_backport-0.2.0.tar.gz", hash = "sha256:68796cfbc7371ebf923f03bdf7bef415f3ec098aeced24e054b253a0e78f7b37"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -4735,19 +4749,19 @@ tests = ["aiohttp", "duckdb", "pandas (>=1.4)", "polars (>=0.19)", "pytest", "py [[package]] name = "langchain" -version = "0.2.11" +version = "0.2.10" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.11-py3-none-any.whl", hash = "sha256:5a7a8b4918f3d3bebce9b4f23b92d050699e6f7fb97591e8941177cf07a260a2"}, - {file = "langchain-0.2.11.tar.gz", hash = "sha256:d7a9e4165f02dca0bd78addbc2319d5b9286b5d37c51d784124102b57e9fd297"}, + {file = "langchain-0.2.10-py3-none-any.whl", hash = "sha256:b4fb58c7faf4f4999cfe3325474979a7121a1737dd101655a723a1d957ef0617"}, + {file = "langchain-0.2.10.tar.gz", hash = "sha256:1f861c1b59ac9c91b02bb0fa58d3adad1c1d0686636872b5b357bbce3ce41d06"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} -langchain-core = ">=0.2.23,<0.3.0" +langchain-core = ">=0.2.22,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" numpy = [ @@ -4851,20 +4865,20 @@ langchain-community = ["langchain-community (>=0.2.4)"] [[package]] name = "langchain-community" -version = "0.2.10" +version = "0.2.9" description = "Community contributed LangChain integrations." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_community-0.2.10-py3-none-any.whl", hash = "sha256:9f4d1b5ab7f0b0a704f538e26e50fce45a461da6d2bf6b7b636d24f22fbc088a"}, - {file = "langchain_community-0.2.10.tar.gz", hash = "sha256:3a0404bad4bd07d6f86affdb62fb3d080a456c66191754d586a409d9d6024d62"}, + {file = "langchain_community-0.2.9-py3-none-any.whl", hash = "sha256:b51d3adf9346a1161c1098917585b9e303cf24e2f5c71f5d232a0504edada5f2"}, + {file = "langchain_community-0.2.9.tar.gz", hash = "sha256:1e7c180232916cbe35fe00509680dd1f805e32d7c87b5e80b3a9ec8754ecae37"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" langchain = ">=0.2.9,<0.3.0" -langchain-core = ">=0.2.23,<0.3.0" +langchain-core = ">=0.2.22,<0.3.0" langsmith = ">=0.1.0,<0.2.0" numpy = [ {version = ">=1,<2", markers = "python_version < \"3.12\""}, @@ -4877,13 +4891,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.24" +version = "0.2.29" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.24-py3-none-any.whl", hash = "sha256:9444fc082d21ef075d925590a684a73fe1f9688a3d90087580ec929751be55e7"}, - {file = "langchain_core-0.2.24.tar.gz", hash = "sha256:f2e3fa200b124e8c45d270da9bf836bed9c09532612c96ff3225e59b9a232f5a"}, + {file = "langchain_core-0.2.29-py3-none-any.whl", hash = "sha256:846c04a3bb72e409a9b928e0eb3ea1762e1473f2c4fb6df2596fbd7b3ab75973"}, + {file = "langchain_core-0.2.29.tar.gz", hash = "sha256:491324745a7afee5a7b285c3904edd9dd0c6efa7daf26b92fec6e84a2d2f5d10"}, ] [package.dependencies] @@ -4896,6 +4910,7 @@ pydantic = [ ] PyYAML = ">=5.3" tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" +typing-extensions = ">=4.7" [[package]] name = "langchain-experimental" @@ -5181,6 +5196,7 @@ asyncer = "^0.0.5" bcrypt = "4.0.1" cachetools = "^5.3.1" chardet = "^5.2.0" +crewai = "^0.36.0" cryptography = "^42.0.5" docstring-parser = "^0.16" duckdb = "^1.0.0" @@ -5220,6 +5236,7 @@ python-multipart = "^0.0.7" rich = "^13.7.0" sentry-sdk = {version = "^2.5.1", extras = ["fastapi", "loguru"]} setuptools = ">=70" +spider-client = "^0.0.27" sqlmodel = "^0.0.18" typer = "^0.12.0" uncurl = "^0.0.11" @@ -5281,13 +5298,13 @@ requests = ">=2,<3" [[package]] name = "langwatch" -version = "0.1.16" +version = "0.1.18" description = "Python SDK for LangWatch for monitoring your LLMs" optional = false -python-versions = "<4.0,>=3.9" +python-versions = "<3.13,>=3.9" files = [ - {file = "langwatch-0.1.16-py3-none-any.whl", hash = "sha256:61ccb1f1efbffc1b2e8bbd3b9c7ed53440d3a66b9fd741f3d1a30d31d0b936f7"}, - {file = "langwatch-0.1.16.tar.gz", hash = "sha256:d8c453a4dcdb500bb55df19ef5fa2c43d450236d84e47fd72348fb3184cc3f6a"}, + {file = "langwatch-0.1.18-py3-none-any.whl", hash = "sha256:73e469fee96d1bebfc54f27b8413d2f108124139ca2df7510063e3a1ea2dc3c1"}, + {file = "langwatch-0.1.18.tar.gz", hash = "sha256:a0e6dbfedca02502bf5abafd7cf9ef8d1bffd8c0d6bac0d932d1209c97752c0e"}, ] [package.dependencies] @@ -5309,13 +5326,13 @@ openai = ["openai (>=1.3.7,<2.0.0)"] [[package]] name = "litellm" -version = "1.42.5" +version = "1.41.25" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.42.5-py3-none-any.whl", hash = "sha256:c8c2f9e40b5aa1c2dcfcac9adb854b8ac22ce2112825d742d8fce516d26e9a65"}, - {file = "litellm-1.42.5.tar.gz", hash = "sha256:64ea24040751009e70e816e9340c5c82717d9a309f4480e5ece9f3f67328e04e"}, + {file = "litellm-1.41.25-py3-none-any.whl", hash = "sha256:80ef35f141402be4ef106a9c720169f6f613ff47df717ab3d1b8ba845c2a5b38"}, + {file = "litellm-1.41.25.tar.gz", hash = "sha256:f6f000b8e666b51914436c26659d4a91d67b350bcc44e47b3837d3b8f0e1640d"}, ] [package.dependencies] @@ -5736,20 +5753,23 @@ files = [ [[package]] name = "mem0ai" -version = "0.0.9" +version = "0.0.5" description = "Long-term memory for AI Agents" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "mem0ai-0.0.9-py3-none-any.whl", hash = "sha256:d4de435729af4fd3d597d022ffb2af89a0630d6c3b4769792bbe27d2ce816858"}, - {file = "mem0ai-0.0.9.tar.gz", hash = "sha256:e4374d5d04aa3f543cd3325f700e4b62f5358ae1c6fa5c44b2ff790c10c4e5f1"}, + {file = "mem0ai-0.0.5-py3-none-any.whl", hash = "sha256:6f6e5356fd522adf0510322cd581476ea456fd7ccefca11b5ac050e9a6f00f36"}, + {file = "mem0ai-0.0.5.tar.gz", hash = "sha256:f2ac35d15e4e620becb8d06b8ebeb1ffa85fac0b7cb2d3138056babec48dd5dd"}, ] [package.dependencies] +boto3 = ">=1.34.144,<2.0.0" +groq = ">=0.9.0,<0.10.0" openai = ">=1.33.0,<2.0.0" posthog = ">=3.5.0,<4.0.0" pydantic = ">=2.7.3,<3.0.0" qdrant-client = ">=1.9.1,<2.0.0" +together = ">=1.2.1,<2.0.0" [[package]] name = "metal-sdk" @@ -6566,13 +6586,13 @@ sympy = "*" [[package]] name = "openai" -version = "1.37.1" +version = "1.37.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.37.1-py3-none-any.whl", hash = "sha256:9a6adda0d6ae8fce02d235c5671c399cfa40d6a281b3628914c7ebf244888ee3"}, - {file = "openai-1.37.1.tar.gz", hash = "sha256:faf87206785a6b5d9e34555d6a3242482a6852bc802e453e2a891f68ee04ce55"}, + {file = "openai-1.37.0-py3-none-any.whl", hash = "sha256:a903245c0ecf622f2830024acdaa78683c70abb8e9d37a497b851670864c9f73"}, + {file = "openai-1.37.0.tar.gz", hash = "sha256:dc8197fc40ab9d431777b6620d962cc49f4544ffc3011f03ce0a805e6eb54adb"}, ] [package.dependencies] @@ -8316,13 +8336,13 @@ files = [ [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, + {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, ] [package.dependencies] @@ -9729,13 +9749,13 @@ tornado = ["tornado (>=6)"] [[package]] name = "setuptools" -version = "72.1.0" +version = "71.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, + {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, ] [package.extras] @@ -9861,6 +9881,19 @@ files = [ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] +[[package]] +name = "spider-client" +version = "0.0.27" +description = "Python SDK for Spider Cloud API" +optional = false +python-versions = "*" +files = [ + {file = "spider-client-0.0.27.tar.gz", hash = "sha256:c3feaf5c491bd9a6c509efa0c8789452497073d9f68e70fc90e7626a6a8365aa"}, +] + +[package.dependencies] +requests = "*" + [[package]] name = "sqlalchemy" version = "2.0.31" @@ -10204,6 +10237,34 @@ requests = ">=2.26.0" [package.extras] blobfile = ["blobfile (>=2)"] +[[package]] +name = "together" +version = "1.2.6" +description = "Python client for Together's Cloud Platform!" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "together-1.2.6-py3-none-any.whl", hash = "sha256:b3ccf467919edcf3a3927dcf7aad6dee95c4a276ced7bff523a2b361fc766d56"}, + {file = "together-1.2.6.tar.gz", hash = "sha256:f79f383d258fc964809ebe60870c94f2104c15b34451c5b4808bd11d956a1702"}, +] + +[package.dependencies] +aiohttp = ">=3.9.3,<4.0.0" +click = ">=8.1.7,<9.0.0" +eval-type-backport = ">=0.1.3,<0.3.0" +filelock = ">=3.13.1,<4.0.0" +numpy = [ + {version = ">=1.23.5", markers = "python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +pillow = ">=10.3.0,<11.0.0" +pyarrow = ">=10.0.1" +pydantic = ">=2.6.3,<3.0.0" +requests = ">=2.31.0,<3.0.0" +tabulate = ">=0.9.0,<0.10.0" +tqdm = ">=4.66.2,<5.0.0" +typer = ">=0.9,<0.13" + [[package]] name = "tokenizers" version = "0.19.1" @@ -10761,13 +10822,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "71.1.0.20240726" +version = "71.0.0.20240722" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-71.1.0.20240726.tar.gz", hash = "sha256:85ba28e9461bb1be86ebba4db0f1c2408f2b11115b1966334ea9dc464e29303e"}, - {file = "types_setuptools-71.1.0.20240726-py3-none-any.whl", hash = "sha256:a7775376f36e0ff09bcad236bf265777590a66b11623e48c20bfc30f1444ea36"}, + {file = "types-setuptools-71.0.0.20240722.tar.gz", hash = "sha256:8f1fd5281945ed8f5a896f05dd50bc31917d6e2487ff9508f4bac522d13ad395"}, + {file = "types_setuptools-71.0.0.20240722-py3-none-any.whl", hash = "sha256:04a383bd1a2dcdb6a85397516ce2d7b46617d89f1d758f686d0d9069943d9811"}, ] [[package]] @@ -11331,13 +11392,13 @@ files = [ [[package]] name = "weaviate-client" -version = "4.7.1" +version = "4.6.7" description = "A python native Weaviate client" optional = false python-versions = ">=3.8" files = [ - {file = "weaviate_client-4.7.1-py3-none-any.whl", hash = "sha256:342f5c67b126cee4dc3a60467ad1ae74971cd5614e27af6fb13d687a345352c4"}, - {file = "weaviate_client-4.7.1.tar.gz", hash = "sha256:af99ac4e53613d2ff5b797372e95d004d0c8a1dd10a7f592068bcb423a30af30"}, + {file = "weaviate_client-4.6.7-py3-none-any.whl", hash = "sha256:8793de35264cab33a84fe8cb8c422a257fe4d8334657aaddd8ead853da3fb34a"}, + {file = "weaviate_client-4.6.7.tar.gz", hash = "sha256:202b32e160536f5f44e4a635d30c3d3a0790b1a7ff997f5e243919d1ac5b68a1"}, ] [package.dependencies] @@ -12012,4 +12073,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "b0b58a9883d3eacc262701a7938ff839365386ba5e155dd2582986501d2b5d7c" +content-hash = "6554abddd0322e28c531774986c897c560099a974f985aa48b21b36b12e2be52" diff --git a/pyproject.toml b/pyproject.toml index f29810c7a88c..337431790852 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,6 +106,7 @@ langchain-google-community = "1.0.7" wolframalpha = "^5.1.3" astra-assistants = "^2.0.15" composio-langchain = "^0.3.28" +spider-client = "^0.0.27" [tool.poetry.group.dev.dependencies] diff --git a/src/backend/base/langflow/base/langchain_utilities/spider_constants.py b/src/backend/base/langflow/base/langchain_utilities/spider_constants.py new file mode 100644 index 000000000000..8630e942e020 --- /dev/null +++ b/src/backend/base/langflow/base/langchain_utilities/spider_constants.py @@ -0,0 +1 @@ +MODES = ["scrape", "crawl"] diff --git a/src/backend/base/langflow/components/embeddings/AstraVectorize.py b/src/backend/base/langflow/components/embeddings/AstraVectorize.py index d90bc051c8c3..4de49eb758c9 100644 --- a/src/backend/base/langflow/components/embeddings/AstraVectorize.py +++ b/src/backend/base/langflow/components/embeddings/AstraVectorize.py @@ -1,6 +1,7 @@ from typing import Any + from langflow.custom import Component -from langflow.inputs.inputs import DictInput, SecretStrInput, MessageTextInput, DropdownInput +from langflow.inputs.inputs import DictInput, DropdownInput, MessageTextInput, SecretStrInput from langflow.template.field.base import Output @@ -60,13 +61,19 @@ class AstraVectorizeComponent(Component): name="model_name", display_name="Model Name", info=f"The embedding model to use for the selected provider. Each provider has a different set of models " - f"available (https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\n\n{VECTORIZE_MODELS_STR}", + f"available (full list at https://docs.datastax.com/en/astra-db-serverless/databases/embedding-generation.html):\n\n{VECTORIZE_MODELS_STR}", required=True, ), MessageTextInput( name="api_key_name", - display_name="Provider API Key Name", - info="The name of the embeddings provider API key stored on Astra.", + display_name="API Key name", + info="The name of the embeddings provider API key stored on Astra. If set, it will override the 'ProviderKey' in the authentication parameters.", + ), + DictInput( + name="authentication", + display_name="Authentication parameters", + is_list=True, + advanced=True, ), SecretStrInput( name="provider_api_key", diff --git a/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py b/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py new file mode 100644 index 000000000000..ee4b732735c5 --- /dev/null +++ b/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py @@ -0,0 +1,121 @@ +from spider.spider import Spider # type: ignore + +from langflow.base.langchain_utilities.spider_constants import MODES +from langflow.custom import Component +from langflow.io import BoolInput, DictInput, DropdownInput, IntInput, Output, SecretStrInput, StrInput +from langflow.schema import Data + + +class SpiderTool(Component): + display_name: str = "Spider Web Crawler & Scraper" + description: str = "Spider API for web crawling and scraping." + output_types: list[str] = ["Document"] + documentation: str = "https://spider.cloud/docs/api" + + inputs = [ + SecretStrInput( + name="spider_api_key", + display_name="Spider API Key", + required=True, + password=True, + info="The Spider API Key, get it from https://spider.cloud", + ), + StrInput( + name="url", + display_name="URL", + required=True, + info="The URL to scrape or crawl", + ), + DropdownInput( + name="mode", + display_name="Mode", + required=True, + options=MODES, + value=MODES[0], + info="The mode of operation: scrape or crawl", + ), + IntInput( + name="limit", + display_name="Limit", + info="The maximum amount of pages allowed to crawl per website. Set to 0 to crawl all pages.", + advanced=True, + ), + IntInput( + name="depth", + display_name="Depth", + info="The crawl limit for maximum depth. If 0, no limit will be applied.", + advanced=True, + ), + StrInput( + name="blacklist", + display_name="Blacklist", + info="Blacklist paths that you do not want to crawl. Use Regex patterns.", + advanced=True, + ), + StrInput( + name="whitelist", + display_name="Whitelist", + info="Whitelist paths that you want to crawl, ignoring all other routes. Use Regex patterns.", + advanced=True, + ), + BoolInput( + name="use_readability", + display_name="Use Readability", + info="Use readability to pre-process the content for reading.", + advanced=True, + ), + IntInput( + name="request_timeout", + display_name="Request Timeout", + info="Timeout for the request in seconds.", + advanced=True, + ), + BoolInput( + name="metadata", + display_name="Metadata", + info="Include metadata in the response.", + advanced=True, + ), + DictInput( + name="params", + display_name="Additional Parameters", + info="Additional parameters to pass to the API. If provided, other inputs will be ignored.", + ), + ] + + outputs = [ + Output(display_name="Markdown", name="content", method="crawl"), + ] + + def crawl(self) -> list[Data]: + if self.params: + parameters = self.params.data + else: + parameters = { + "limit": self.limit, + "depth": self.depth, + "blacklist": self.blacklist, + "whitelist": self.whitelist, + "use_readability": self.use_readability, + "request_timeout": self.request_timeout, + "metadata": self.metadata, + "return_format": "markdown", + } + + app = Spider(api_key=self.spider_api_key) + try: + if self.mode == "scrape": + parameters["limit"] = 1 + result = app.scrape_url(self.url, parameters) + elif self.mode == "crawl": + result = app.crawl_url(self.url, parameters) + else: + raise ValueError(f"Invalid mode: {self.mode}. Must be 'scrape' or 'crawl'.") + except Exception as e: + raise Exception(f"Error: {str(e)}") + + records = [] + + for record in result: + records.append(Data(data={"content": record["content"], "url": record["url"]})) + return records diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index ca723ea299da..e4a2611f8eec 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -157,7 +157,7 @@ async def flatten_query_string_lists(request: Request, call_next): raise ValueError(f"Invalid port number {prome_port_str}") if settings.prometheus_enabled: - from prometheus_client import start_http_server + from prometheus_client import start_http_server # type: ignore start_http_server(settings.prometheus_port) diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index 0ac1f4607ff3..e52b0c5b7754 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -185,6 +185,17 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.23)"] +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + [[package]] name = "appnope" version = "0.1.4" @@ -275,6 +286,17 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + [[package]] name = "bcrypt" version = "4.0.1" @@ -309,6 +331,27 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "blinker" version = "1.8.2" @@ -320,6 +363,44 @@ files = [ {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, ] +[[package]] +name = "boto3" +version = "1.34.145" +description = "The AWS SDK for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "boto3-1.34.145-py3-none-any.whl", hash = "sha256:69d5afb7a017d07dd6bdfb680d2912d5d369b3fafa0a45161207d9f393b14d7e"}, + {file = "boto3-1.34.145.tar.gz", hash = "sha256:ac770fb53dde1743aec56bd8e56b7ee2e2f5ad42a37825968ec4ff8428822640"}, +] + +[package.dependencies] +botocore = ">=1.34.145,<1.35.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.34.145" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">=3.8" +files = [ + {file = "botocore-1.34.145-py3-none-any.whl", hash = "sha256:2e72e262de02adcb0264ac2bac159a28f55dbba8d9e52aa0308773a42950dff5"}, + {file = "botocore-1.34.145.tar.gz", hash = "sha256:edf0fb4c02186ae29b76263ac5fda18b0a085d334a310551c9984407cf1079e6"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.20.11)"] + [[package]] name = "brotli" version = "1.1.0" @@ -412,6 +493,31 @@ files = [ {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, ] +[[package]] +name = "build" +version = "1.2.1" +description = "A simple, correct Python build frontend" +optional = false +python-versions = ">=3.8" +files = [ + {file = "build-1.2.1-py3-none-any.whl", hash = "sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4"}, + {file = "build-1.2.1.tar.gz", hash = "sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "os_name == \"nt\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} +packaging = ">=19.1" +pyproject_hooks = "*" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"] +test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"] +typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"] +uv = ["uv (>=0.1.18)"] +virtualenv = ["virtualenv (>=20.0.35)"] + [[package]] name = "cachetools" version = "5.4.0" @@ -634,6 +740,84 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "chroma-hnswlib" +version = "0.7.3" +description = "Chromas fork of hnswlib" +optional = false +python-versions = "*" +files = [ + {file = "chroma-hnswlib-0.7.3.tar.gz", hash = "sha256:b6137bedde49fffda6af93b0297fe00429fc61e5a072b1ed9377f909ed95a932"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59d6a7c6f863c67aeb23e79a64001d537060b6995c3eca9a06e349ff7b0998ca"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d71a3f4f232f537b6152947006bd32bc1629a8686df22fd97777b70f416c127a"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c92dc1ebe062188e53970ba13f6b07e0ae32e64c9770eb7f7ffa83f149d4210"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49da700a6656fed8753f68d44b8cc8ae46efc99fc8a22a6d970dc1697f49b403"}, + {file = "chroma_hnswlib-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:108bc4c293d819b56476d8f7865803cb03afd6ca128a2a04d678fffc139af029"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11e7ca93fb8192214ac2b9c0943641ac0daf8f9d4591bb7b73be808a83835667"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f552e4d23edc06cdeb553cdc757d2fe190cdeb10d43093d6a3319f8d4bf1c6b"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f96f4d5699e486eb1fb95849fe35ab79ab0901265805be7e60f4eaa83ce263ec"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:368e57fe9ebae05ee5844840fa588028a023d1182b0cfdb1d13f607c9ea05756"}, + {file = "chroma_hnswlib-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:b7dca27b8896b494456db0fd705b689ac6b73af78e186eb6a42fea2de4f71c6f"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:70f897dc6218afa1d99f43a9ad5eb82f392df31f57ff514ccf4eeadecd62f544"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aef10b4952708f5a1381c124a29aead0c356f8d7d6e0b520b778aaa62a356f4"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee2d8d1529fca3898d512079144ec3e28a81d9c17e15e0ea4665697a7923253"}, + {file = "chroma_hnswlib-0.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:a4021a70e898783cd6f26e00008b494c6249a7babe8774e90ce4766dd288c8ba"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a8f61fa1d417fda848e3ba06c07671f14806a2585272b175ba47501b066fe6b1"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7563be58bc98e8f0866907368e22ae218d6060601b79c42f59af4eccbbd2e0a"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51b8d411486ee70d7b66ec08cc8b9b6620116b650df9c19076d2d8b6ce2ae914"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d706782b628e4f43f1b8a81e9120ac486837fbd9bcb8ced70fe0d9b95c72d77"}, + {file = "chroma_hnswlib-0.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:54f053dedc0e3ba657f05fec6e73dd541bc5db5b09aa8bc146466ffb734bdc86"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e607c5a71c610a73167a517062d302c0827ccdd6e259af6e4869a5c1306ffb5d"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2358a795870156af6761890f9eb5ca8cade57eb10c5f046fe94dae1faa04b9e"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cea425df2e6b8a5e201fff0d922a1cc1d165b3cfe762b1408075723c8892218"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:454df3dd3e97aa784fba7cf888ad191e0087eef0fd8c70daf28b753b3b591170"}, + {file = "chroma_hnswlib-0.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:df587d15007ca701c6de0ee7d5585dd5e976b7edd2b30ac72bc376b3c3f85882"}, +] + +[package.dependencies] +numpy = "*" + +[[package]] +name = "chromadb" +version = "0.4.24" +description = "Chroma." +optional = false +python-versions = ">=3.8" +files = [ + {file = "chromadb-0.4.24-py3-none-any.whl", hash = "sha256:3a08e237a4ad28b5d176685bd22429a03717fe09d35022fb230d516108da01da"}, + {file = "chromadb-0.4.24.tar.gz", hash = "sha256:a5c80b4e4ad9b236ed2d4899a5b9e8002b489293f2881cb2cadab5b199ee1c72"}, +] + +[package.dependencies] +bcrypt = ">=4.0.1" +build = ">=1.0.3" +chroma-hnswlib = "0.7.3" +fastapi = ">=0.95.2" +grpcio = ">=1.58.0" +importlib-resources = "*" +kubernetes = ">=28.1.0" +mmh3 = ">=4.0.1" +numpy = ">=1.22.5" +onnxruntime = ">=1.14.1" +opentelemetry-api = ">=1.2.0" +opentelemetry-exporter-otlp-proto-grpc = ">=1.2.0" +opentelemetry-instrumentation-fastapi = ">=0.41b0" +opentelemetry-sdk = ">=1.2.0" +orjson = ">=3.9.12" +overrides = ">=7.3.1" +posthog = ">=2.4.0" +pulsar-client = ">=3.1.0" +pydantic = ">=1.9" +pypika = ">=0.48.9" +PyYAML = ">=6.0.0" +requests = ">=2.28" +tenacity = ">=8.2.3" +tokenizers = ">=0.13.2" +tqdm = ">=4.65.0" +typer = ">=0.9.0" +typing-extensions = ">=4.5.0" +uvicorn = {version = ">=0.18.3", extras = ["standard"]} + [[package]] name = "click" version = "8.1.7" @@ -648,6 +832,29 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "cohere" +version = "5.6.2" +description = "" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "cohere-5.6.2-py3-none-any.whl", hash = "sha256:cfecf1343bcaa4091266c5a231fbcb3ccbd80cad05ea093ef80024a117aa3a2f"}, + {file = "cohere-5.6.2.tar.gz", hash = "sha256:6bb901afdfb02f62ad8ed2d82f12d8ea87a6869710f5f880cb89190c4e994805"}, +] + +[package.dependencies] +boto3 = ">=1.34.0,<2.0.0" +fastavro = ">=1.9.4,<2.0.0" +httpx = ">=0.21.2" +httpx-sse = ">=0.4.0,<0.5.0" +parameterized = ">=0.9.0,<0.10.0" +pydantic = ">=1.9.2" +requests = ">=2.0.0,<3.0.0" +tokenizers = ">=0.15,<1" +types-requests = ">=2.0.0,<3.0.0" +typing_extensions = ">=4.0.0" + [[package]] name = "colorama" version = "0.4.6" @@ -659,6 +866,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coloredlogs" +version = "15.0.1" +description = "Colored terminal output for Python's logging module" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, + {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, +] + +[package.dependencies] +humanfriendly = ">=9.1" + +[package.extras] +cron = ["capturer (>=2.4)"] + [[package]] name = "comm" version = "0.2.2" @@ -778,6 +1002,36 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "crewai" +version = "0.36.1" +description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks." +optional = false +python-versions = "<=3.13,>=3.10" +files = [ + {file = "crewai-0.36.1-py3-none-any.whl", hash = "sha256:dbaa50d102542ea0c790bd62511b35234b2f5fa8d2333a6598beb84f407f0e00"}, + {file = "crewai-0.36.1.tar.gz", hash = "sha256:ea50ec5d3ef2df85e1b520efd9331bebb49ed7143e6cd1feec645da49217d2b0"}, +] + +[package.dependencies] +appdirs = ">=1.4.4,<2.0.0" +click = ">=8.1.7,<9.0.0" +embedchain = ">=0.1.114,<0.2.0" +instructor = "1.3.3" +jsonref = ">=1.1.0,<2.0.0" +langchain = ">0.2,<=0.3" +openai = ">=1.13.3,<2.0.0" +opentelemetry-api = ">=1.22.0,<2.0.0" +opentelemetry-exporter-otlp-proto-http = ">=1.22.0,<2.0.0" +opentelemetry-sdk = ">=1.22.0,<2.0.0" +pydantic = ">=2.4.2,<3.0.0" +python-dotenv = ">=1.0.0,<2.0.0" +regex = ">=2023.12.25,<2024.0.0" + +[package.extras] +agentops = ["agentops (>=0.1.9,<0.2.0)"] +tools = ["crewai-tools (>=0.4.7,<0.5.0)"] + [[package]] name = "cryptography" version = "42.0.8" @@ -965,6 +1219,17 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + [[package]] name = "dnspython" version = "2.6.1" @@ -1084,6 +1349,57 @@ files = [ dnspython = ">=2.0.0" idna = ">=2.0.0" +[[package]] +name = "embedchain" +version = "0.1.118" +description = "Simplest open source retrieval (RAG) framework" +optional = false +python-versions = "<=3.13,>=3.9" +files = [ + {file = "embedchain-0.1.118-py3-none-any.whl", hash = "sha256:38ead471df9d9234bf42e6f7a32cab26431d50d6f2f894f18a6cabc0b02bf31a"}, + {file = "embedchain-0.1.118.tar.gz", hash = "sha256:1fa1e799882a1dc4e63af344595b043f1c1f30fbd59461b6660b1934b85a1e4b"}, +] + +[package.dependencies] +alembic = ">=1.13.1,<2.0.0" +beautifulsoup4 = ">=4.12.2,<5.0.0" +chromadb = ">=0.4.24,<0.5.0" +cohere = ">=5.3,<6.0" +google-cloud-aiplatform = ">=1.26.1,<2.0.0" +gptcache = ">=0.1.43,<0.2.0" +langchain = ">0.2,<=0.3" +langchain-cohere = ">=0.1.4,<0.2.0" +langchain-community = ">=0.2.6,<0.3.0" +langchain-openai = ">=0.1.7,<0.2.0" +mem0ai = ">=0.0.5,<0.0.6" +openai = ">=1.1.1" +posthog = ">=3.0.2,<4.0.0" +pypdf = ">=4.0.1,<5.0.0" +pysbd = ">=0.3.4,<0.4.0" +python-dotenv = ">=1.0.0,<2.0.0" +rich = ">=13.7.0,<14.0.0" +schema = ">=0.7.5,<0.8.0" +sqlalchemy = ">=2.0.27,<3.0.0" +tiktoken = ">=0.7.0,<0.8.0" + +[package.extras] +elasticsearch = ["elasticsearch (>=8.9.0,<9.0.0)"] +gmail = ["google-api-core (>=2.15.0,<3.0.0)", "google-api-python-client (>=2.111.0,<3.0.0)", "google-auth (>=2.25.2,<3.0.0)", "google-auth-httplib2 (>=0.2.0,<0.3.0)", "google-auth-oauthlib (>=1.2.0,<2.0.0)", "requests (>=2.31.0,<3.0.0)"] +google = ["google-generativeai (>=0.3.0,<0.4.0)"] +googledrive = ["google-api-python-client (>=2.111.0,<3.0.0)", "google-auth-httplib2 (>=0.2.0,<0.3.0)", "google-auth-oauthlib (>=1.2.0,<2.0.0)"] +lancedb = ["lancedb (>=0.6.2,<0.7.0)"] +llama2 = ["replicate (>=0.15.4,<0.16.0)"] +milvus = ["pymilvus (==2.4.3)"] +mistralai = ["langchain-mistralai (>=0.1.9,<0.2.0)"] +mysql = ["mysql-connector-python (>=8.1.0,<9.0.0)"] +opensearch = ["opensearch-py (==2.3.1)"] +opensource = ["gpt4all (==2.0.2)", "sentence-transformers (>=2.2.2,<3.0.0)", "torch (==2.3.0)"] +postgres = ["psycopg (>=3.1.12,<4.0.0)", "psycopg-binary (>=3.1.12,<4.0.0)", "psycopg-pool (>=3.1.8,<4.0.0)"] +qdrant = ["qdrant-client (>=1.6.3,<2.0.0)"] +together = ["together (>=1.2.1,<2.0.0)"] +vertexai = ["langchain-google-vertexai (>=1.0.6,<2.0.0)"] +weaviate = ["weaviate-client (>=3.24.1,<4.0.0)"] + [[package]] name = "emoji" version = "2.12.1" @@ -1101,6 +1417,20 @@ typing-extensions = ">=4.7.0" [package.extras] dev = ["coverage", "pytest (>=7.4.4)"] +[[package]] +name = "eval-type-backport" +version = "0.2.0" +description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." +optional = false +python-versions = ">=3.8" +files = [ + {file = "eval_type_backport-0.2.0-py3-none-any.whl", hash = "sha256:ac2f73d30d40c5a30a80b8739a789d6bb5e49fdffa66d7912667e2015d9c9933"}, + {file = "eval_type_backport-0.2.0.tar.gz", hash = "sha256:68796cfbc7371ebf923f03bdf7bef415f3ec098aeced24e054b253a0e78f7b37"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -1186,6 +1516,52 @@ uvicorn = {version = ">=0.15.0", extras = ["standard"]} [package.extras] standard = ["uvicorn[standard] (>=0.15.0)"] +[[package]] +name = "fastavro" +version = "1.9.5" +description = "Fast read/write of AVRO files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastavro-1.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:61253148e95dd2b6457247b441b7555074a55de17aef85f5165bfd5facf600fc"}, + {file = "fastavro-1.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b604935d671ad47d888efc92a106f98e9440874108b444ac10e28d643109c937"}, + {file = "fastavro-1.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0adbf4956fd53bd74c41e7855bb45ccce953e0eb0e44f5836d8d54ad843f9944"}, + {file = "fastavro-1.9.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:53d838e31457db8bf44460c244543f75ed307935d5fc1d93bc631cc7caef2082"}, + {file = "fastavro-1.9.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:07b6288e8681eede16ff077632c47395d4925c2f51545cd7a60f194454db2211"}, + {file = "fastavro-1.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:ef08cf247fdfd61286ac0c41854f7194f2ad05088066a756423d7299b688d975"}, + {file = "fastavro-1.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c52d7bb69f617c90935a3e56feb2c34d4276819a5c477c466c6c08c224a10409"}, + {file = "fastavro-1.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85e05969956003df8fa4491614bc62fe40cec59e94d06e8aaa8d8256ee3aab82"}, + {file = "fastavro-1.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06e6df8527493a9f0d9a8778df82bab8b1aa6d80d1b004e5aec0a31dc4dc501c"}, + {file = "fastavro-1.9.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:27820da3b17bc01cebb6d1687c9d7254b16d149ef458871aaa207ed8950f3ae6"}, + {file = "fastavro-1.9.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:195a5b8e33eb89a1a9b63fa9dce7a77d41b3b0cd785bac6044df619f120361a2"}, + {file = "fastavro-1.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:be612c109efb727bfd36d4d7ed28eb8e0506617b7dbe746463ebbf81e85eaa6b"}, + {file = "fastavro-1.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b133456c8975ec7d2a99e16a7e68e896e45c821b852675eac4ee25364b999c14"}, + {file = "fastavro-1.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf586373c3d1748cac849395aad70c198ee39295f92e7c22c75757b5c0300fbe"}, + {file = "fastavro-1.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:724ef192bc9c55d5b4c7df007f56a46a21809463499856349d4580a55e2b914c"}, + {file = "fastavro-1.9.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bfd11fe355a8f9c0416803afac298960eb4c603a23b1c74ff9c1d3e673ea7185"}, + {file = "fastavro-1.9.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9827d1654d7bcb118ef5efd3e5b2c9ab2a48d44dac5e8c6a2327bc3ac3caa828"}, + {file = "fastavro-1.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:d84b69dca296667e6137ae7c9a96d060123adbc0c00532cc47012b64d38b47e9"}, + {file = "fastavro-1.9.5-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:fb744e9de40fb1dc75354098c8db7da7636cba50a40f7bef3b3fb20f8d189d88"}, + {file = "fastavro-1.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:240df8bacd13ff5487f2465604c007d686a566df5cbc01d0550684eaf8ff014a"}, + {file = "fastavro-1.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3bb35c25bbc3904e1c02333bc1ae0173e0a44aa37a8e95d07e681601246e1f1"}, + {file = "fastavro-1.9.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b47a54a9700de3eabefd36dabfb237808acae47bc873cada6be6990ef6b165aa"}, + {file = "fastavro-1.9.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:48c7b5e6d2f3bf7917af301c275b05c5be3dd40bb04e80979c9e7a2ab31a00d1"}, + {file = "fastavro-1.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:05d13f98d4e325be40387e27da9bd60239968862fe12769258225c62ec906f04"}, + {file = "fastavro-1.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5b47948eb196263f6111bf34e1cd08d55529d4ed46eb50c1bc8c7c30a8d18868"}, + {file = "fastavro-1.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85b7a66ad521298ad9373dfe1897a6ccfc38feab54a47b97922e213ae5ad8870"}, + {file = "fastavro-1.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44cb154f863ad80e41aea72a709b12e1533b8728c89b9b1348af91a6154ab2f5"}, + {file = "fastavro-1.9.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b5f7f2b1fe21231fd01f1a2a90e714ae267fe633cd7ce930c0aea33d1c9f4901"}, + {file = "fastavro-1.9.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88fbbe16c61d90a89d78baeb5a34dc1c63a27b115adccdbd6b1fb6f787deacf2"}, + {file = "fastavro-1.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:753f5eedeb5ca86004e23a9ce9b41c5f25eb64a876f95edcc33558090a7f3e4b"}, + {file = "fastavro-1.9.5.tar.gz", hash = "sha256:6419ebf45f88132a9945c51fe555d4f10bb97c236288ed01894f957c6f914553"}, +] + +[package.extras] +codecs = ["cramjam", "lz4", "zstandard"] +lz4 = ["lz4"] +snappy = ["cramjam"] +zstandard = ["zstandard"] + [[package]] name = "filelock" version = "3.15.4" @@ -1267,6 +1643,17 @@ files = [ Flask = ">=1.0.4" Werkzeug = ">=1.0.1" +[[package]] +name = "flatbuffers" +version = "24.3.25" +description = "The FlatBuffers serialization format for Python" +optional = false +python-versions = "*" +files = [ + {file = "flatbuffers-24.3.25-py2.py3-none-any.whl", hash = "sha256:8dbdec58f935f3765e4f7f3cf635ac3a77f83568138d6a2311f524ec96364812"}, + {file = "flatbuffers-24.3.25.tar.gz", hash = "sha256:de2ec5b203f21441716617f38443e0a8ebf3d25bf0d9c0bb0ce68fa00ad546a4"}, +] + [[package]] name = "frozenlist" version = "1.4.1" @@ -1353,6 +1740,45 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "fsspec" +version = "2024.6.1" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, + {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + [[package]] name = "gevent" version = "24.2.1" @@ -1502,6 +1928,313 @@ benchmarks = ["httplib2", "httpx", "requests", "urllib3"] dev = ["dpkt", "pytest", "requests"] examples = ["oauth2"] +[[package]] +name = "google-api-core" +version = "2.19.1" +description = "Google API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd"}, + {file = "google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = [ + {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +proto-plus = ">=1.22.3,<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,<6.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.32.0" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"}, + {file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-cloud-aiplatform" +version = "1.59.0" +description = "Vertex AI API client library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "google-cloud-aiplatform-1.59.0.tar.gz", hash = "sha256:2bebb59c0ba3e3b4b568305418ca1b021977988adbee8691a5bed09b037e7e63"}, + {file = "google_cloud_aiplatform-1.59.0-py2.py3-none-any.whl", hash = "sha256:549e6eb1844b0f853043309138ebe2db00de4bbd8197b3bde26804ac163ef52a"}, +] + +[package.dependencies] +docstring-parser = "<1" +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.8.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +google-cloud-bigquery = ">=1.15.0,<3.20.0 || >3.20.0,<4.0.0dev" +google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" +google-cloud-storage = ">=1.32.0,<3.0.0dev" +packaging = ">=14.3" +proto-plus = ">=1.22.3,<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" +pydantic = "<3" +shapely = "<3.0.0dev" + +[package.extras] +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 (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)"] +endpoint = ["requests (>=2.28.1)"] +full = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"] +langchain = ["langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "tenacity (<=8.3)"] +langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "langchain (>=0.1.16,<0.3)", "langchain-core (<0.2)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "tenacity (<=8.3)"] +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,<7)"] +prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<=0.109.1)", "httpx (>=0.23.0,<0.25.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] +preview = ["cloudpickle (<3.0)", "google-cloud-logging (<4.0)"] +private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] +rapid-evaluation = ["pandas (>=1.0.0,<2.2.0)", "tqdm (>=4.23.0)"] +ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "setuptools (<70.0.0)"] +ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "ray[train] (==2.9.3)", "scikit-learn", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"] +reasoningengine = ["cloudpickle (>=3.0,<4.0)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)"] +tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] +testing = ["bigframes", "cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.1)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nltk", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "sentencepiece (>=0.2.0)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (==2.13.0)", "tensorflow (==2.16.1)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "torch (>=2.2.0)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] +tokenization = ["sentencepiece (>=0.2.0)"] +vizier = ["google-vizier (>=0.1.6)"] +xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] + +[[package]] +name = "google-cloud-bigquery" +version = "3.25.0" +description = "Google BigQuery API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-bigquery-3.25.0.tar.gz", hash = "sha256:5b2aff3205a854481117436836ae1403f11f2594e6810a98886afd57eda28509"}, + {file = "google_cloud_bigquery-3.25.0-py2.py3-none-any.whl", hash = "sha256:7f0c371bc74d2a7fb74dacbc00ac0f90c8c2bec2289b51dd6685a275873b1ce9"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0dev" +google-cloud-core = ">=1.6.0,<3.0.0dev" +google-resumable-media = ">=0.6.0,<3.0dev" +packaging = ">=20.0.0" +python-dateutil = ">=2.7.2,<3.0dev" +requests = ">=2.21.0,<3.0.0dev" + +[package.extras] +all = ["Shapely (>=1.8.4,<3.0.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "importlib-metadata (>=1.0.0)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "proto-plus (>=1.15.0,<2.0.0dev)", "protobuf (>=3.19.5,!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] +bigquery-v2 = ["proto-plus (>=1.15.0,<2.0.0dev)", "protobuf (>=3.19.5,!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev)"] +bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] +geopandas = ["Shapely (>=1.8.4,<3.0.0dev)", "geopandas (>=0.9.0,<1.0dev)"] +ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] +ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] +opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] +pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "importlib-metadata (>=1.0.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] +tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] + +[[package]] +name = "google-cloud-core" +version = "2.4.1" +description = "Google Cloud API client core library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073"}, + {file = "google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61"}, +] + +[package.dependencies] +google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.25.0,<3.0dev" + +[package.extras] +grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] + +[[package]] +name = "google-cloud-resource-manager" +version = "1.12.4" +description = "Google Cloud Resource Manager API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-resource-manager-1.12.4.tar.gz", hash = "sha256:3eda914a925e92465ef80faaab7e0f7a9312d486dd4e123d2c76e04bac688ff0"}, + {file = "google_cloud_resource_manager-1.12.4-py2.py3-none-any.whl", hash = "sha256:0b6663585f7f862166c0fb4c55fdda721fce4dc2dc1d5b52d03ee4bf2653a85f"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.20.2,<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,<6.0.0dev" + +[[package]] +name = "google-cloud-storage" +version = "2.17.0" +description = "Google Cloud Storage API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-cloud-storage-2.17.0.tar.gz", hash = "sha256:49378abff54ef656b52dca5ef0f2eba9aa83dc2b2c72c78714b03a1a95fe9388"}, + {file = "google_cloud_storage-2.17.0-py2.py3-none-any.whl", hash = "sha256:5b393bc766b7a3bc6f5407b9e665b2450d36282614b7945e570b3480a456d1e1"}, +] + +[package.dependencies] +google-api-core = ">=2.15.0,<3.0.0dev" +google-auth = ">=2.26.1,<3.0dev" +google-cloud-core = ">=2.3.0,<3.0dev" +google-crc32c = ">=1.0,<2.0dev" +google-resumable-media = ">=2.6.0" +requests = ">=2.18.0,<3.0.0dev" + +[package.extras] +protobuf = ["protobuf (<5.0.0dev)"] + +[[package]] +name = "google-crc32c" +version = "1.5.0" +description = "A python wrapper of the C library 'Google CRC32C'" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, + {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, + {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, + {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, + {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, + {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, + {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, + {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, + {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, + {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, + {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, + {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, + {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, + {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, + {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, + {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, + {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, + {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, + {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, + {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, + {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, +] + +[package.extras] +testing = ["pytest"] + +[[package]] +name = "google-resumable-media" +version = "2.7.1" +description = "Utilities for Google Media Downloads and Resumable Uploads" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-resumable-media-2.7.1.tar.gz", hash = "sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33"}, + {file = "google_resumable_media-2.7.1-py2.py3-none-any.whl", hash = "sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c"}, +] + +[package.dependencies] +google-crc32c = ">=1.0,<2.0dev" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "google-auth (>=1.22.0,<2.0dev)"] +requests = ["requests (>=2.18.0,<3.0.0dev)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.63.2" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, + {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, +] + +[package.dependencies] +grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.20.2,<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,<6.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + [[package]] name = "gprof2dot" version = "2024.6.6" @@ -1513,6 +2246,22 @@ files = [ {file = "gprof2dot-2024.6.6.tar.gz", hash = "sha256:fa1420c60025a9eb7734f65225b4da02a10fc6dd741b37fa129bc6b41951e5ab"}, ] +[[package]] +name = "gptcache" +version = "0.1.43" +description = "GPTCache, a powerful caching library that can be used to speed up and lower the cost of chat applications that rely on the LLM service. GPTCache works as a memcache for AIGC applications, similar to how Redis works for traditional applications." +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "gptcache-0.1.43-py3-none-any.whl", hash = "sha256:9c557ec9cc14428942a0ebf1c838520dc6d2be801d67bb6964807043fc2feaf5"}, + {file = "gptcache-0.1.43.tar.gz", hash = "sha256:cebe7ec5e32a3347bf839e933a34e67c7fcae620deaa7cb8c6d7d276c8686f1a"}, +] + +[package.dependencies] +cachetools = "*" +numpy = "*" +requests = "*" + [[package]] name = "grandalf" version = "0.8" @@ -1601,6 +2350,183 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "groq" +version = "0.9.0" +description = "The official Python library for the groq API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "groq-0.9.0-py3-none-any.whl", hash = "sha256:d0e46f4ad645504672bb09c8100af3ced3a7db0d5119dc13e4aca535fc455874"}, + {file = "groq-0.9.0.tar.gz", hash = "sha256:130ed5e35d3acfaab46b9e7a078eeaebf91052f4a9d71f86f87fb319b5fec332"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +typing-extensions = ">=4.7,<5" + +[[package]] +name = "grpc-google-iam-v1" +version = "0.13.1" +description = "IAM API client library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpc-google-iam-v1-0.13.1.tar.gz", hash = "sha256:3ff4b2fd9d990965e410965253c0da6f66205d5a8291c4c31c6ebecca18a9001"}, + {file = "grpc_google_iam_v1-0.13.1-py2.py3-none-any.whl", hash = "sha256:c3e86151a981811f30d5e7330f271cee53e73bb87755e88cc3b6f0c7b5fe374e"}, +] + +[package.dependencies] +googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0dev" +protobuf = ">=3.20.2,<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,<6.0.0dev" + +[[package]] +name = "grpcio" +version = "1.65.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.65.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:3dc5f928815b8972fb83b78d8db5039559f39e004ec93ebac316403fe031a062"}, + {file = "grpcio-1.65.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8333ca46053c35484c9f2f7e8d8ec98c1383a8675a449163cea31a2076d93de8"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7af64838b6e615fff0ec711960ed9b6ee83086edfa8c32670eafb736f169d719"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb64b4166362d9326f7efbf75b1c72106c1aa87f13a8c8b56a1224fac152f5c"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8422dc13ad93ec8caa2612b5032a2b9cd6421c13ed87f54db4a3a2c93afaf77"}, + {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4effc0562b6c65d4add6a873ca132e46ba5e5a46f07c93502c37a9ae7f043857"}, + {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a6c71575a2fedf259724981fd73a18906513d2f306169c46262a5bae956e6364"}, + {file = "grpcio-1.65.1-cp310-cp310-win32.whl", hash = "sha256:34966cf526ef0ea616e008d40d989463e3db157abb213b2f20c6ce0ae7928875"}, + {file = "grpcio-1.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:ca931de5dd6d9eb94ff19a2c9434b23923bce6f767179fef04dfa991f282eaad"}, + {file = "grpcio-1.65.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:bbb46330cc643ecf10bd9bd4ca8e7419a14b6b9dedd05f671c90fb2c813c6037"}, + {file = "grpcio-1.65.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d827a6fb9215b961eb73459ad7977edb9e748b23e3407d21c845d1d8ef6597e5"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:6e71aed8835f8d9fbcb84babc93a9da95955d1685021cceb7089f4f1e717d719"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1c84560b3b2d34695c9ba53ab0264e2802721c530678a8f0a227951f453462"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27adee2338d697e71143ed147fe286c05810965d5d30ec14dd09c22479bfe48a"}, + {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f62652ddcadc75d0e7aa629e96bb61658f85a993e748333715b4ab667192e4e8"}, + {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:71a05fd814700dd9cb7d9a507f2f6a1ef85866733ccaf557eedacec32d65e4c2"}, + {file = "grpcio-1.65.1-cp311-cp311-win32.whl", hash = "sha256:b590f1ad056294dfaeac0b7e1b71d3d5ace638d8dd1f1147ce4bd13458783ba8"}, + {file = "grpcio-1.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:12e9bdf3b5fd48e5fbe5b3da382ad8f97c08b47969f3cca81dd9b36b86ed39e2"}, + {file = "grpcio-1.65.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:54cb822e177374b318b233e54b6856c692c24cdbd5a3ba5335f18a47396bac8f"}, + {file = "grpcio-1.65.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aaf3c54419a28d45bd1681372029f40e5bfb58e5265e3882eaf21e4a5f81a119"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:557de35bdfbe8bafea0a003dbd0f4da6d89223ac6c4c7549d78e20f92ead95d9"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bfd95ef3b097f0cc86ade54eafefa1c8ed623aa01a26fbbdcd1a3650494dd11"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e6a8f3d6c41e6b642870afe6cafbaf7b61c57317f9ec66d0efdaf19db992b90"}, + {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1faaf7355ceed07ceaef0b9dcefa4c98daf1dd8840ed75c2de128c3f4a4d859d"}, + {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:60f1f38eed830488ad2a1b11579ef0f345ff16fffdad1d24d9fbc97ba31804ff"}, + {file = "grpcio-1.65.1-cp312-cp312-win32.whl", hash = "sha256:e75acfa52daf5ea0712e8aa82f0003bba964de7ae22c26d208cbd7bc08500177"}, + {file = "grpcio-1.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff5a84907e51924973aa05ed8759210d8cdae7ffcf9e44fd17646cf4a902df59"}, + {file = "grpcio-1.65.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:1fbd6331f18c3acd7e09d17fd840c096f56eaf0ef830fbd50af45ae9dc8dfd83"}, + {file = "grpcio-1.65.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de5b6be29116e094c5ef9d9e4252e7eb143e3d5f6bd6d50a78075553ab4930b0"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e4a3cdba62b2d6aeae6027ae65f350de6dc082b72e6215eccf82628e79efe9ba"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941c4869aa229d88706b78187d60d66aca77fe5c32518b79e3c3e03fc26109a2"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f40cebe5edb518d78b8131e87cb83b3ee688984de38a232024b9b44e74ee53d3"}, + {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2ca684ba331fb249d8a1ce88db5394e70dbcd96e58d8c4b7e0d7b141a453dce9"}, + {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8558f0083ddaf5de64a59c790bffd7568e353914c0c551eae2955f54ee4b857f"}, + {file = "grpcio-1.65.1-cp38-cp38-win32.whl", hash = "sha256:8d8143a3e3966f85dce6c5cc45387ec36552174ba5712c5dc6fcc0898fb324c0"}, + {file = "grpcio-1.65.1-cp38-cp38-win_amd64.whl", hash = "sha256:76e81a86424d6ca1ce7c16b15bdd6a964a42b40544bf796a48da241fdaf61153"}, + {file = "grpcio-1.65.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb5175f45c980ff418998723ea1b3869cce3766d2ab4e4916fbd3cedbc9d0ed3"}, + {file = "grpcio-1.65.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b12c1aa7b95abe73b3e04e052c8b362655b41c7798da69f1eaf8d186c7d204df"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3019fb50128b21a5e018d89569ffaaaa361680e1346c2f261bb84a91082eb3d3"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ae15275ed98ea267f64ee9ddedf8ecd5306a5b5bb87972a48bfe24af24153e8"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f096ffb881f37e8d4f958b63c74bfc400c7cebd7a944b027357cd2fb8d91a57"}, + {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2f56b5a68fdcf17a0a1d524bf177218c3c69b3947cb239ea222c6f1867c3ab68"}, + {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:941596d419b9736ab548aa0feb5bbba922f98872668847bf0720b42d1d227b9e"}, + {file = "grpcio-1.65.1-cp39-cp39-win32.whl", hash = "sha256:5fd7337a823b890215f07d429f4f193d24b80d62a5485cf88ee06648591a0c57"}, + {file = "grpcio-1.65.1-cp39-cp39-win_amd64.whl", hash = "sha256:1bceeec568372cbebf554eae1b436b06c2ff24cfaf04afade729fb9035408c6c"}, + {file = "grpcio-1.65.1.tar.gz", hash = "sha256:3c492301988cd720cd145d84e17318d45af342e29ef93141228f9cd73222368b"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.65.1)"] + +[[package]] +name = "grpcio-status" +version = "1.62.2" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.62.2" +protobuf = ">=4.21.6" + +[[package]] +name = "grpcio-tools" +version = "1.62.2" +description = "Protobuf code generator for gRPC" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-tools-1.62.2.tar.gz", hash = "sha256:5fd5e1582b678e6b941ee5f5809340be5e0724691df5299aae8226640f94e18f"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:1679b4903aed2dc5bd8cb22a452225b05dc8470a076f14fd703581efc0740cdb"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:9d41e0e47dd075c075bb8f103422968a65dd0d8dc8613288f573ae91eb1053ba"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:987e774f74296842bbffd55ea8826370f70c499e5b5f71a8cf3103838b6ee9c3"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40cd4eeea4b25bcb6903b82930d579027d034ba944393c4751cdefd9c49e6989"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6746bc823958499a3cf8963cc1de00072962fb5e629f26d658882d3f4c35095"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2ed775e844566ce9ce089be9a81a8b928623b8ee5820f5e4d58c1a9d33dfc5ae"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bdc5dd3f57b5368d5d661d5d3703bcaa38bceca59d25955dff66244dbc987271"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-win32.whl", hash = "sha256:3a8d6f07e64c0c7756f4e0c4781d9d5a2b9cc9cbd28f7032a6fb8d4f847d0445"}, + {file = "grpcio_tools-1.62.2-cp310-cp310-win_amd64.whl", hash = "sha256:e33b59fb3efdddeb97ded988a871710033e8638534c826567738d3edce528752"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:472505d030135d73afe4143b0873efe0dcb385bd6d847553b4f3afe07679af00"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:ec674b4440ef4311ac1245a709e87b36aca493ddc6850eebe0b278d1f2b6e7d1"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:184b4174d4bd82089d706e8223e46c42390a6ebac191073b9772abc77308f9fa"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c195d74fe98541178ece7a50dad2197d43991e0f77372b9a88da438be2486f12"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a34d97c62e61bfe9e6cff0410fe144ac8cca2fc979ad0be46b7edf026339d161"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cbb8453ae83a1db2452b7fe0f4b78e4a8dd32be0f2b2b73591ae620d4d784d3d"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f989e5cebead3ae92c6abf6bf7b19949e1563a776aea896ac5933f143f0c45d"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-win32.whl", hash = "sha256:c48fabe40b9170f4e3d7dd2c252e4f1ff395dc24e49ac15fc724b1b6f11724da"}, + {file = "grpcio_tools-1.62.2-cp311-cp311-win_amd64.whl", hash = "sha256:8c616d0ad872e3780693fce6a3ac8ef00fc0963e6d7815ce9dcfae68ba0fc287"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:10cc3321704ecd17c93cf68c99c35467a8a97ffaaed53207e9b2da6ae0308ee1"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:9be84ff6d47fd61462be7523b49d7ba01adf67ce4e1447eae37721ab32464dd8"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:d82f681c9a9d933a9d8068e8e382977768e7779ddb8870fa0cf918d8250d1532"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04c607029ae3660fb1624ed273811ffe09d57d84287d37e63b5b802a35897329"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72b61332f1b439c14cbd3815174a8f1d35067a02047c32decd406b3a09bb9890"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8214820990d01b52845f9fbcb92d2b7384a0c321b303e3ac614c219dc7d1d3af"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:462e0ab8dd7c7b70bfd6e3195eebc177549ede5cf3189814850c76f9a340d7ce"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-win32.whl", hash = "sha256:fa107460c842e4c1a6266150881694fefd4f33baa544ea9489601810c2210ef8"}, + {file = "grpcio_tools-1.62.2-cp312-cp312-win_amd64.whl", hash = "sha256:759c60f24c33a181bbbc1232a6752f9b49fbb1583312a4917e2b389fea0fb0f2"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:45db5da2bcfa88f2b86b57ef35daaae85c60bd6754a051d35d9449c959925b57"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:ab84bae88597133f6ea7a2bdc57b2fda98a266fe8d8d4763652cbefd20e73ad7"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:7a49bccae1c7d154b78e991885c3111c9ad8c8fa98e91233de425718f47c6139"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7e439476b29d6dac363b321781a113794397afceeb97dad85349db5f1cb5e9a"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ea369c4d1567d1acdf69c8ea74144f4ccad9e545df7f9a4fc64c94fa7684ba3"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f955702dc4b530696375251319d05223b729ed24e8673c2129f7a75d2caefbb"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3708a747aa4b6b505727282ca887041174e146ae030ebcadaf4c1d346858df62"}, + {file = "grpcio_tools-1.62.2-cp37-cp37m-win_amd64.whl", hash = "sha256:2ce149ea55eadb486a7fb75a20f63ef3ac065ee6a0240ed25f3549ce7954c653"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:58cbb24b3fa6ae35aa9c210fcea3a51aa5fef0cd25618eb4fd94f746d5a9b703"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:6413581e14a80e0b4532577766cf0586de4dd33766a31b3eb5374a746771c07d"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:47117c8a7e861382470d0e22d336e5a91fdc5f851d1db44fa784b9acea190d87"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f1ba79a253df9e553d20319c615fa2b429684580fa042dba618d7f6649ac7e4"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04a394cf5e51ba9be412eb9f6c482b6270bd81016e033e8eb7d21b8cc28fe8b5"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3c53b221378b035ae2f1881cbc3aca42a6075a8e90e1a342c2f205eb1d1aa6a1"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c384c838b34d1b67068e51b5bbe49caa6aa3633acd158f1ab16b5da8d226bc53"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-win32.whl", hash = "sha256:19ea69e41c3565932aa28a202d1875ec56786aea46a2eab54a3b28e8a27f9517"}, + {file = "grpcio_tools-1.62.2-cp38-cp38-win_amd64.whl", hash = "sha256:1d768a5c07279a4c461ebf52d0cec1c6ca85c6291c71ec2703fe3c3e7e28e8c4"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:5b07b5874187e170edfbd7aa2ca3a54ebf3b2952487653e8c0b0d83601c33035"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:d58389fe8be206ddfb4fa703db1e24c956856fcb9a81da62b13577b3a8f7fda7"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:7d8b4e00c3d7237b92260fc18a561cd81f1da82e8be100db1b7d816250defc66"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fe08d2038f2b7c53259b5c49e0ad08c8e0ce2b548d8185993e7ef67e8592cca"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19216e1fb26dbe23d12a810517e1b3fbb8d4f98b1a3fbebeec9d93a79f092de4"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b8574469ecc4ff41d6bb95f44e0297cdb0d95bade388552a9a444db9cd7485cd"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4f6f32d39283ea834a493fccf0ebe9cfddee7577bdcc27736ad4be1732a36399"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-win32.whl", hash = "sha256:76eb459bdf3fb666e01883270beee18f3f11ed44488486b61cd210b4e0e17cc1"}, + {file = "grpcio_tools-1.62.2-cp39-cp39-win_amd64.whl", hash = "sha256:217c2ee6a7ce519a55958b8622e21804f6fdb774db08c322f4c9536c35fdce7c"}, +] + +[package.dependencies] +grpcio = ">=1.62.2" +protobuf = ">=4.21.6,<5.0dev" +setuptools = "*" + [[package]] name = "gunicorn" version = "22.0.0" @@ -1633,6 +2559,32 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "h2" +version = "4.1.0" +description = "HTTP/2 State-Machine based protocol implementation" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, + {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, +] + +[package.dependencies] +hpack = ">=4.0,<5" +hyperframe = ">=6.0,<7" + +[[package]] +name = "hpack" +version = "4.0.0" +description = "Pure-Python HPACK header compression" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, + {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, +] + [[package]] name = "httpcore" version = "1.0.5" @@ -1716,6 +2668,7 @@ files = [ [package.dependencies] anyio = "*" certifi = "*" +h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} httpcore = "==1.*" idna = "*" sniffio = "*" @@ -1726,6 +2679,76 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +[[package]] +name = "httpx-sse" +version = "0.4.0" +description = "Consume Server-Sent Event (SSE) messages with HTTPX." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, + {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, +] + +[[package]] +name = "huggingface-hub" +version = "0.24.0" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.24.0-py3-none-any.whl", hash = "sha256:7ad92edefb93d8145c061f6df8d99df2ff85f8379ba5fac8a95aca0642afa5d7"}, + {file = "huggingface_hub-0.24.0.tar.gz", hash = "sha256:6c7092736b577d89d57b3cdfea026f1b0dc2234ae783fa0d59caf1bf7d52dfa7"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + +[[package]] +name = "humanfriendly" +version = "10.0" +description = "Human friendly output for text interfaces using Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, + {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, +] + +[package.dependencies] +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} + +[[package]] +name = "hyperframe" +version = "6.0.1" +description = "HTTP/2 framing layer for Python" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, + {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, +] + [[package]] name = "identify" version = "2.6.0" @@ -1770,6 +2793,21 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +[[package]] +name = "importlib-resources" +version = "6.4.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, + {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1781,6 +2819,38 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "instructor" +version = "1.3.3" +description = "structured outputs for llm" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "instructor-1.3.3-py3-none-any.whl", hash = "sha256:94b114b39a1181fa348d162e6e4ff5c4d985324736020c0233fed5d4db444dbd"}, + {file = "instructor-1.3.3.tar.gz", hash = "sha256:e27bf3c1187b0b2130ea38ecde7c2b4f571d6a5ce1397fb15c27490988b45441"}, +] + +[package.dependencies] +aiohttp = ">=3.9.1,<4.0.0" +docstring-parser = ">=0.16,<0.17" +jiter = ">=0.4.1,<0.5.0" +openai = ">=1.1.0,<2.0.0" +pydantic = ">=2.7.0,<3.0.0" +pydantic-core = ">=2.18.0,<3.0.0" +rich = ">=13.7.0,<14.0.0" +tenacity = ">=8.2.3,<9.0.0" +typer = ">=0.9.0,<1.0.0" + +[package.extras] +anthropic = ["anthropic (>=0.27.0,<0.28.0)", "xmltodict (>=0.13.0,<0.14.0)"] +cohere = ["cohere (>=5.1.8,<6.0.0)"] +google-generativeai = ["google-generativeai (>=0.5.4,<0.6.0)"] +groq = ["groq (>=0.4.2,<0.5.0)"] +litellm = ["litellm (>=1.35.31,<2.0.0)"] +mistralai = ["mistralai (>=0.1.8,<0.2.0)"] +test-docs = ["anthropic (>=0.27.0,<0.28.0)", "cohere (>=5.1.8,<6.0.0)", "diskcache (>=5.6.3,<6.0.0)", "fastapi (>=0.109.2,<0.110.0)", "groq (>=0.4.2,<0.5.0)", "litellm (>=1.35.31,<2.0.0)", "mistralai (>=0.1.8,<0.2.0)", "pandas (>=2.2.0,<3.0.0)", "pydantic_extra_types (>=2.6.0,<3.0.0)", "redis (>=5.0.1,<6.0.0)", "tabulate (>=0.9.0,<0.10.0)"] +vertexai = ["google-cloud-aiplatform (>=1.52.0,<2.0.0)", "jsonref (>=1.1.0,<2.0.0)"] + [[package]] name = "ipykernel" version = "6.29.5" @@ -1899,6 +2969,87 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jiter" +version = "0.4.2" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jiter-0.4.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c2b003ff58d14f5e182b875acd5177b2367245c19a03be9a2230535d296f7550"}, + {file = "jiter-0.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b48c77c25f094707731cd5bad6b776046846b60a27ee20efc8fadfb10a89415f"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f50ad6b172bde4d45f4d4ea10c49282a337b8bb735afc99763dfa55ea84a743"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95f6001e86f525fbbc9706db2078dc22be078b0950de55b92d37041930f5f940"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16646ef23b62b007de80460d303ebb2d81e355dac9389c787cec87cdd7ffef2f"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b4e847c13b0bf1255c711a92330e7a8cb8b5cdd1e37d7db309627bcdd3367ff"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c536589be60e4c5f2b20fadc4db7e9f55d4c9df3551f29ddf1c4a18dcc9dd54"}, + {file = "jiter-0.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3b2763996167830889a854b4ded30bb90897f9b76be78069c50c3ec4540950e"}, + {file = "jiter-0.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:675e8ab98c99495091af6b6e9bf2b6353bcf81f25ab6ce27d36127e315b4505d"}, + {file = "jiter-0.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e48e43d9d999aaf55f53406b8846ff8cbe3e47ee4b9dc37e5a10a65ce760809f"}, + {file = "jiter-0.4.2-cp310-none-win32.whl", hash = "sha256:881b6e67c50bc36acb3570eda693763c8cd77d590940e06fa6d325d0da52ec1b"}, + {file = "jiter-0.4.2-cp310-none-win_amd64.whl", hash = "sha256:bb8f7b43259efc6add0d721ade2953e064b24e2026d26d979bc09ec080844cef"}, + {file = "jiter-0.4.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:24ad336ac47f274fa83f6fbedcabff9d3387c80f67c66b992688e6a8ba2c47e9"}, + {file = "jiter-0.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fc392a220095730afe365ce1516f2f88bb085a2fd29ea191be9c6e3c71713d9a"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1fdc408de36c81460896de0176f2f7b9f3574dcd35693a0b2c00f4ca34c98e4"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c10ad76722ee6a8c820b0db06a793c08b7d679e5201b9563015bd1e06c959a09"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbb46d1e9c82bba87f0cbda38413e49448a7df35b1e55917124bff9f38974a23"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:194e28ef4b5f3b61408cb2ee6b6dcbcdb0c9063d01b92b01345b7605692849f5"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0a447533eccd62748a727e058efa10a8d7cf1de8ffe1a4d705ecb41dad9090"}, + {file = "jiter-0.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5f7704d7260bbb88cca3453951af739589132b26e896a3144fa2dae2263716d7"}, + {file = "jiter-0.4.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:01427458bc9550f2eda09d425755330e7d0eb09adce099577433bebf05d28d59"}, + {file = "jiter-0.4.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159b8416879c0053b17c352f70b67b749ef5b2924c6154318ecf71918aab0905"}, + {file = "jiter-0.4.2-cp311-none-win32.whl", hash = "sha256:f2445234acfb79048ce1a0d5d0e181abb9afd9e4a29d8d9988fe26cc5773a81a"}, + {file = "jiter-0.4.2-cp311-none-win_amd64.whl", hash = "sha256:e15a65f233b6b0e5ac10ddf3b97ceb18aa9ffba096259961641d78b4ee321bd5"}, + {file = "jiter-0.4.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d61d59521aea9745447ce50f74d39a16ef74ec9d6477d9350d77e75a3d774ad2"}, + {file = "jiter-0.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eef607dc0acc251923427808dbd017f1998ae3c1a0430a261527aa5cbb3a942"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af6bf39954646e374fc47429c656372ac731a6a26b644158a5a84bcdbed33a47"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f509d23606e476852ee46a2b65b5c4ad3905f17424d9cc19c1dffa1c94ba3c6"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59672774daa44ee140aada0c781c82bee4d9ac5e522966186cfb6b3c217d8a51"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24a0458efac5afeca254cf557b8a654e17013075a69905c78f88d557f129d871"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8860766d1c293e75c1bb4e25b74fa987e3adf199cac3f5f9e6e49c2bebf092f"}, + {file = "jiter-0.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a109f3281b72bbf4921fe43db1005c004a38559ca0b6c4985add81777dfe0a44"}, + {file = "jiter-0.4.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:faa7e667454b77ad2f0ef87db39f4944de759617aadf210ea2b73f26bb24755f"}, + {file = "jiter-0.4.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3512f8b00cafb6780b427cb6282800d2bf8277161d9c917830661bd4ed1d3528"}, + {file = "jiter-0.4.2-cp312-none-win32.whl", hash = "sha256:853b35d508ee5b66d06630473c1c0b7bb5e29bf4785c9d2202437116c94f7e21"}, + {file = "jiter-0.4.2-cp312-none-win_amd64.whl", hash = "sha256:4a3a8197784278eb8b24cb02c45e1cad67c2ce5b5b758adfb19b87f74bbdff9c"}, + {file = "jiter-0.4.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ca2a4d750aed3154b89f2efb148609fc985fad8db739460797aaf9b478acedda"}, + {file = "jiter-0.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0e6c304b3cc6896256727e1fb8991c7179a345eca8224e201795e9cacf4683b0"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cc34ac708ae1750d077e490321761ec4b9a055b994cbdd1d6fbd37099e4aa7b"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c93383875ab8d2e4f760aaff335b4a12ff32d4f9cf49c4498d657734f611466"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce197ee044add576afca0955b42142dd0312639adb6ebadbdbe4277f2855614f"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a427716813ff65480ca5b5117cfa099f49b49cd38051f8609bd0d5493013ca0"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:479990218353356234669e70fac53e5eb6f739a10db25316171aede2c97d9364"}, + {file = "jiter-0.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d35a91ec5ac74cf33234c431505299fa91c0a197c2dbafd47400aca7c69489d4"}, + {file = "jiter-0.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b27189847193708c94ad10ca0d891309342ae882725d2187cf5d2db02bde8d1b"}, + {file = "jiter-0.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76c255308cd1093fb411a03756b7bb220e48d4a98c30cbc79ed448bf3978e27d"}, + {file = "jiter-0.4.2-cp38-none-win32.whl", hash = "sha256:bb77438060bad49cc251941e6701b31138365c8a0ddaf10cdded2fcc6dd30701"}, + {file = "jiter-0.4.2-cp38-none-win_amd64.whl", hash = "sha256:ce858af19f7ce0d4b51c9f6c0c9d08f1e9dcef1986c5875efd0674a7054292ca"}, + {file = "jiter-0.4.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:6128838a2f357b3921b2a3242d5dc002ae4255ecc8f9f05c20d56d7d2d79c5ad"}, + {file = "jiter-0.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f2420cebb9ba856cb57dcab1d2d8def949b464b0db09c22a4e4dbd52fff7b200"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5d13d8128e853b320e00bb18bd4bb8b136cc0936091dc87633648fc688eb705"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eba5d6e54f149c508ba88677f97d3dc7dd75e9980d234bbac8027ac6db0763a3"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fad5d64af0bc0545237419bf4150d8de56f0bd217434bdd1a59730327252bef"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d179e7bca89cf5719bd761dd37a341ff0f98199ecaa9c14af09792e47e977cc"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36353caee9f103d8ee7bda077f6400505b0f370e27eabcab33a33d21de12a2a6"}, + {file = "jiter-0.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dd146c25bce576ca5db64fc7eccb8862af00f1f0e30108796953f12a53660e4c"}, + {file = "jiter-0.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:14b7c08cadbcd703041c66dc30e24e17de2f340281cac0e69374223ecf153aa4"}, + {file = "jiter-0.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a90f1a8b3d29aea198f8ea2b01148276ced8056e5103f32525266b3d880e65c9"}, + {file = "jiter-0.4.2-cp39-none-win32.whl", hash = "sha256:25b174997c780337b61ae57b1723455eecae9a17a9659044fd3c3b369190063f"}, + {file = "jiter-0.4.2-cp39-none-win_amd64.whl", hash = "sha256:bef62cea18521c5b99368147040c7e560c55098a35c93456f110678a2d34189a"}, + {file = "jiter-0.4.2.tar.gz", hash = "sha256:29b9d44f23f0c05f46d482f4ebf03213ee290d77999525d0975a17f875bf1eea"}, +] + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + [[package]] name = "jq" version = "1.7.0" @@ -2006,6 +3157,17 @@ files = [ {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, ] +[[package]] +name = "jsonref" +version = "1.1.0" +description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, + {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, +] + [[package]] name = "jupyter-client" version = "8.6.2" @@ -2048,21 +3210,47 @@ traitlets = ">=5.3" docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] +[[package]] +name = "kubernetes" +version = "30.1.0" +description = "Kubernetes python client" +optional = false +python-versions = ">=3.6" +files = [ + {file = "kubernetes-30.1.0-py2.py3-none-any.whl", hash = "sha256:e212e8b7579031dd2e512168b617373bc1e03888d41ac4e04039240a292d478d"}, + {file = "kubernetes-30.1.0.tar.gz", hash = "sha256:41e4c77af9f28e7a6c314e3bd06a8c6229ddd787cad684e0ab9f69b498e98ebc"}, +] + +[package.dependencies] +certifi = ">=14.05.14" +google-auth = ">=1.0.1" +oauthlib = ">=3.2.2" +python-dateutil = ">=2.5.3" +pyyaml = ">=5.4.1" +requests = "*" +requests-oauthlib = "*" +six = ">=1.9.0" +urllib3 = ">=1.24.2" +websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" + +[package.extras] +adal = ["adal (>=1.0.2)"] + [[package]] name = "langchain" -version = "0.2.12" +version = "0.2.10" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.12-py3-none-any.whl", hash = "sha256:565d2f5df1c06815d1c684400218ec4ae5e1027887aad343226fad846c54e726"}, - {file = "langchain-0.2.12.tar.gz", hash = "sha256:fe7bd409c133017446fec54c38a5e7cb14f74e020090d7b5065374badf71e6d1"}, + {file = "langchain-0.2.10-py3-none-any.whl", hash = "sha256:b4fb58c7faf4f4999cfe3325474979a7121a1737dd101655a723a1d957ef0617"}, + {file = "langchain-0.2.10.tar.gz", hash = "sha256:1f861c1b59ac9c91b02bb0fa58d3adad1c1d0686636872b5b357bbce3ce41d06"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} -langchain-core = ">=0.2.27,<0.3.0" +langchain-core = ">=0.2.22,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" numpy = [ @@ -2075,22 +3263,43 @@ requests = ">=2,<3" SQLAlchemy = ">=1.4,<3" tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" +[[package]] +name = "langchain-cohere" +version = "0.1.9" +description = "An integration package connecting Cohere and LangChain" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_cohere-0.1.9-py3-none-any.whl", hash = "sha256:96d6a15125797319474ac84b54024e5024f3f5fc45032ebf228d95d6998c9b13"}, + {file = "langchain_cohere-0.1.9.tar.gz", hash = "sha256:549620d23bc3d77f62d1045787095fe2c1cfa233dba69455139f9a2f65f952fa"}, +] + +[package.dependencies] +cohere = ">=5.5.6,<6.0" +langchain-core = ">=0.2.2,<0.3" +langchain-experimental = ">=0.0.6" +pandas = ">=1.4.3" +tabulate = ">=0.9.0,<0.10.0" + +[package.extras] +langchain-community = ["langchain-community (>=0.2.4)"] + [[package]] name = "langchain-community" -version = "0.2.11" +version = "0.2.9" description = "Community contributed LangChain integrations." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_community-0.2.11-py3-none-any.whl", hash = "sha256:465c03ba1603975d141533424185e09546ecf09e379c93aee2671bdc9b325cda"}, - {file = "langchain_community-0.2.11.tar.gz", hash = "sha256:ede261ff8202f1433f004ee90baf89f371cee37cb1abfc16dd0f8392db10b23e"}, + {file = "langchain_community-0.2.9-py3-none-any.whl", hash = "sha256:b51d3adf9346a1161c1098917585b9e303cf24e2f5c71f5d232a0504edada5f2"}, + {file = "langchain_community-0.2.9.tar.gz", hash = "sha256:1e7c180232916cbe35fe00509680dd1f805e32d7c87b5e80b3a9ec8754ecae37"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" -langchain = ">=0.2.12,<0.3.0" -langchain-core = ">=0.2.27,<0.3.0" +langchain = ">=0.2.9,<0.3.0" +langchain-core = ">=0.2.22,<0.3.0" langsmith = ">=0.1.0,<0.2.0" numpy = [ {version = ">=1,<2", markers = "python_version < \"3.12\""}, @@ -2139,6 +3348,22 @@ files = [ langchain-community = ">=0.2.5,<0.3.0" langchain-core = ">=0.2.7,<0.3.0" +[[package]] +name = "langchain-openai" +version = "0.1.17" +description = "An integration package connecting OpenAI and LangChain" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_openai-0.1.17-py3-none-any.whl", hash = "sha256:30bef5574ecbbbb91b8025b2dc5a1bd81fd62157d3ad1a35d820141f31c5b443"}, + {file = "langchain_openai-0.1.17.tar.gz", hash = "sha256:c5d70ddecdcb93e146f376bdbadbb6ec69de9ac0f402cd5b83de50b655ba85ee"}, +] + +[package.dependencies] +langchain-core = ">=0.2.20,<0.3.0" +openai = ">=1.32.0,<2.0.0" +tiktoken = ">=0.7,<1" + [[package]] name = "langchain-text-splitters" version = "0.2.2" @@ -2171,13 +3396,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0" [[package]] name = "langsmith" -version = "0.1.98" +version = "0.1.93" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.98-py3-none-any.whl", hash = "sha256:f79e8a128652bbcee4606d10acb6236973b5cd7dde76e3741186d3b97b5698e9"}, - {file = "langsmith-0.1.98.tar.gz", hash = "sha256:e07678219a0502e8f26d35294e72127a39d25e32fafd091af5a7bb661e9a6bd1"}, + {file = "langsmith-0.1.93-py3-none-any.whl", hash = "sha256:811210b9d5f108f36431bd7b997eb9476a9ecf5a2abd7ddbb606c1cdcf0f43ce"}, + {file = "langsmith-0.1.93.tar.gz", hash = "sha256:285b6ad3a54f50fa8eb97b5f600acc57d0e37e139dd8cf2111a117d0435ba9b4"}, ] [package.dependencies] @@ -2550,6 +3775,146 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "mem0ai" +version = "0.0.5" +description = "Long-term memory for AI Agents" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "mem0ai-0.0.5-py3-none-any.whl", hash = "sha256:6f6e5356fd522adf0510322cd581476ea456fd7ccefca11b5ac050e9a6f00f36"}, + {file = "mem0ai-0.0.5.tar.gz", hash = "sha256:f2ac35d15e4e620becb8d06b8ebeb1ffa85fac0b7cb2d3138056babec48dd5dd"}, +] + +[package.dependencies] +boto3 = ">=1.34.144,<2.0.0" +groq = ">=0.9.0,<0.10.0" +openai = ">=1.33.0,<2.0.0" +posthog = ">=3.5.0,<4.0.0" +pydantic = ">=2.7.3,<3.0.0" +qdrant-client = ">=1.9.1,<2.0.0" +together = ">=1.2.1,<2.0.0" + +[[package]] +name = "mmh3" +version = "4.1.0" +description = "Python extension for MurmurHash (MurmurHash3), a set of fast and robust hash functions." +optional = false +python-versions = "*" +files = [ + {file = "mmh3-4.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be5ac76a8b0cd8095784e51e4c1c9c318c19edcd1709a06eb14979c8d850c31a"}, + {file = "mmh3-4.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98a49121afdfab67cd80e912b36404139d7deceb6773a83620137aaa0da5714c"}, + {file = "mmh3-4.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5259ac0535874366e7d1a5423ef746e0d36a9e3c14509ce6511614bdc5a7ef5b"}, + {file = "mmh3-4.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5950827ca0453a2be357696da509ab39646044e3fa15cad364eb65d78797437"}, + {file = "mmh3-4.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dd0f652ae99585b9dd26de458e5f08571522f0402155809fd1dc8852a613a39"}, + {file = "mmh3-4.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d25548070942fab1e4a6f04d1626d67e66d0b81ed6571ecfca511f3edf07e6"}, + {file = "mmh3-4.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53db8d9bad3cb66c8f35cbc894f336273f63489ce4ac416634932e3cbe79eb5b"}, + {file = "mmh3-4.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75da0f615eb55295a437264cc0b736753f830b09d102aa4c2a7d719bc445ec05"}, + {file = "mmh3-4.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b926b07fd678ea84b3a2afc1fa22ce50aeb627839c44382f3d0291e945621e1a"}, + {file = "mmh3-4.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c5b053334f9b0af8559d6da9dc72cef0a65b325ebb3e630c680012323c950bb6"}, + {file = "mmh3-4.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5bf33dc43cd6de2cb86e0aa73a1cc6530f557854bbbe5d59f41ef6de2e353d7b"}, + {file = "mmh3-4.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fa7eacd2b830727ba3dd65a365bed8a5c992ecd0c8348cf39a05cc77d22f4970"}, + {file = "mmh3-4.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:42dfd6742b9e3eec599f85270617debfa0bbb913c545bb980c8a4fa7b2d047da"}, + {file = "mmh3-4.1.0-cp310-cp310-win32.whl", hash = "sha256:2974ad343f0d39dcc88e93ee6afa96cedc35a9883bc067febd7ff736e207fa47"}, + {file = "mmh3-4.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:74699a8984ded645c1a24d6078351a056f5a5f1fe5838870412a68ac5e28d865"}, + {file = "mmh3-4.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:f0dc874cedc23d46fc488a987faa6ad08ffa79e44fb08e3cd4d4cf2877c00a00"}, + {file = "mmh3-4.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3280a463855b0eae64b681cd5b9ddd9464b73f81151e87bb7c91a811d25619e6"}, + {file = "mmh3-4.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:97ac57c6c3301769e757d444fa7c973ceb002cb66534b39cbab5e38de61cd896"}, + {file = "mmh3-4.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b6502cdb4dbd880244818ab363c8770a48cdccecf6d729ade0241b736b5ec0"}, + {file = "mmh3-4.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52ba2da04671a9621580ddabf72f06f0e72c1c9c3b7b608849b58b11080d8f14"}, + {file = "mmh3-4.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a5fef4c4ecc782e6e43fbeab09cff1bac82c998a1773d3a5ee6a3605cde343e"}, + {file = "mmh3-4.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5135358a7e00991f73b88cdc8eda5203bf9de22120d10a834c5761dbeb07dd13"}, + {file = "mmh3-4.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cff9ae76a54f7c6fe0167c9c4028c12c1f6de52d68a31d11b6790bb2ae685560"}, + {file = "mmh3-4.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f02576a4d106d7830ca90278868bf0983554dd69183b7bbe09f2fcd51cf54f"}, + {file = "mmh3-4.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:073d57425a23721730d3ff5485e2da489dd3c90b04e86243dd7211f889898106"}, + {file = "mmh3-4.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:71e32ddec7f573a1a0feb8d2cf2af474c50ec21e7a8263026e8d3b4b629805db"}, + {file = "mmh3-4.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7cbb20b29d57e76a58b40fd8b13a9130db495a12d678d651b459bf61c0714cea"}, + {file = "mmh3-4.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a42ad267e131d7847076bb7e31050f6c4378cd38e8f1bf7a0edd32f30224d5c9"}, + {file = "mmh3-4.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a013979fc9390abadc445ea2527426a0e7a4495c19b74589204f9b71bcaafeb"}, + {file = "mmh3-4.1.0-cp311-cp311-win32.whl", hash = "sha256:1d3b1cdad7c71b7b88966301789a478af142bddcb3a2bee563f7a7d40519a00f"}, + {file = "mmh3-4.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0dc6dc32eb03727467da8e17deffe004fbb65e8b5ee2b502d36250d7a3f4e2ec"}, + {file = "mmh3-4.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9ae3a5c1b32dda121c7dc26f9597ef7b01b4c56a98319a7fe86c35b8bc459ae6"}, + {file = "mmh3-4.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0033d60c7939168ef65ddc396611077a7268bde024f2c23bdc283a19123f9e9c"}, + {file = "mmh3-4.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d6af3e2287644b2b08b5924ed3a88c97b87b44ad08e79ca9f93d3470a54a41c5"}, + {file = "mmh3-4.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d82eb4defa245e02bb0b0dc4f1e7ee284f8d212633389c91f7fba99ba993f0a2"}, + {file = "mmh3-4.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba245e94b8d54765e14c2d7b6214e832557e7856d5183bc522e17884cab2f45d"}, + {file = "mmh3-4.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb04e2feeabaad6231e89cd43b3d01a4403579aa792c9ab6fdeef45cc58d4ec0"}, + {file = "mmh3-4.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3b1a27def545ce11e36158ba5d5390cdbc300cfe456a942cc89d649cf7e3b2"}, + {file = "mmh3-4.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce0ab79ff736d7044e5e9b3bfe73958a55f79a4ae672e6213e92492ad5e734d5"}, + {file = "mmh3-4.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b02268be6e0a8eeb8a924d7db85f28e47344f35c438c1e149878bb1c47b1cd3"}, + {file = "mmh3-4.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:deb887f5fcdaf57cf646b1e062d56b06ef2f23421c80885fce18b37143cba828"}, + {file = "mmh3-4.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99dd564e9e2b512eb117bd0cbf0f79a50c45d961c2a02402787d581cec5448d5"}, + {file = "mmh3-4.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:08373082dfaa38fe97aa78753d1efd21a1969e51079056ff552e687764eafdfe"}, + {file = "mmh3-4.1.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:54b9c6a2ea571b714e4fe28d3e4e2db37abfd03c787a58074ea21ee9a8fd1740"}, + {file = "mmh3-4.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a7b1edf24c69e3513f879722b97ca85e52f9032f24a52284746877f6a7304086"}, + {file = "mmh3-4.1.0-cp312-cp312-win32.whl", hash = "sha256:411da64b951f635e1e2284b71d81a5a83580cea24994b328f8910d40bed67276"}, + {file = "mmh3-4.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bebc3ecb6ba18292e3d40c8712482b4477abd6981c2ebf0e60869bd90f8ac3a9"}, + {file = "mmh3-4.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:168473dd608ade6a8d2ba069600b35199a9af837d96177d3088ca91f2b3798e3"}, + {file = "mmh3-4.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:372f4b7e1dcde175507640679a2a8790185bb71f3640fc28a4690f73da986a3b"}, + {file = "mmh3-4.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:438584b97f6fe13e944faf590c90fc127682b57ae969f73334040d9fa1c7ffa5"}, + {file = "mmh3-4.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6e27931b232fc676675fac8641c6ec6b596daa64d82170e8597f5a5b8bdcd3b6"}, + {file = "mmh3-4.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:571a92bad859d7b0330e47cfd1850b76c39b615a8d8e7aa5853c1f971fd0c4b1"}, + {file = "mmh3-4.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a69d6afe3190fa08f9e3a58e5145549f71f1f3fff27bd0800313426929c7068"}, + {file = "mmh3-4.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afb127be0be946b7630220908dbea0cee0d9d3c583fa9114a07156f98566dc28"}, + {file = "mmh3-4.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:940d86522f36348ef1a494cbf7248ab3f4a1638b84b59e6c9e90408bd11ad729"}, + {file = "mmh3-4.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3dcccc4935686619a8e3d1f7b6e97e3bd89a4a796247930ee97d35ea1a39341"}, + {file = "mmh3-4.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01bb9b90d61854dfc2407c5e5192bfb47222d74f29d140cb2dd2a69f2353f7cc"}, + {file = "mmh3-4.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bcb1b8b951a2c0b0fb8a5426c62a22557e2ffc52539e0a7cc46eb667b5d606a9"}, + {file = "mmh3-4.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6477a05d5e5ab3168e82e8b106e316210ac954134f46ec529356607900aea82a"}, + {file = "mmh3-4.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:da5892287e5bea6977364b15712a2573c16d134bc5fdcdd4cf460006cf849278"}, + {file = "mmh3-4.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:99180d7fd2327a6fffbaff270f760576839dc6ee66d045fa3a450f3490fda7f5"}, + {file = "mmh3-4.1.0-cp38-cp38-win32.whl", hash = "sha256:9b0d4f3949913a9f9a8fb1bb4cc6ecd52879730aab5ff8c5a3d8f5b593594b73"}, + {file = "mmh3-4.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:598c352da1d945108aee0c3c3cfdd0e9b3edef74108f53b49d481d3990402169"}, + {file = "mmh3-4.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:475d6d1445dd080f18f0f766277e1237fa2914e5fe3307a3b2a3044f30892103"}, + {file = "mmh3-4.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5ca07c41e6a2880991431ac717c2a049056fff497651a76e26fc22224e8b5732"}, + {file = "mmh3-4.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ebe052fef4bbe30c0548d12ee46d09f1b69035ca5208a7075e55adfe091be44"}, + {file = "mmh3-4.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaefd42e85afb70f2b855a011f7b4d8a3c7e19c3f2681fa13118e4d8627378c5"}, + {file = "mmh3-4.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0ae43caae5a47afe1b63a1ae3f0986dde54b5fb2d6c29786adbfb8edc9edfb"}, + {file = "mmh3-4.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6218666f74c8c013c221e7f5f8a693ac9cf68e5ac9a03f2373b32d77c48904de"}, + {file = "mmh3-4.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac59294a536ba447b5037f62d8367d7d93b696f80671c2c45645fa9f1109413c"}, + {file = "mmh3-4.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:086844830fcd1e5c84fec7017ea1ee8491487cfc877847d96f86f68881569d2e"}, + {file = "mmh3-4.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e42b38fad664f56f77f6fbca22d08450f2464baa68acdbf24841bf900eb98e87"}, + {file = "mmh3-4.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d08b790a63a9a1cde3b5d7d733ed97d4eb884bfbc92f075a091652d6bfd7709a"}, + {file = "mmh3-4.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:73ea4cc55e8aea28c86799ecacebca09e5f86500414870a8abaedfcbaf74d288"}, + {file = "mmh3-4.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f90938ff137130e47bcec8dc1f4ceb02f10178c766e2ef58a9f657ff1f62d124"}, + {file = "mmh3-4.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:aa1f13e94b8631c8cd53259250556edcf1de71738936b60febba95750d9632bd"}, + {file = "mmh3-4.1.0-cp39-cp39-win32.whl", hash = "sha256:a3b680b471c181490cf82da2142029edb4298e1bdfcb67c76922dedef789868d"}, + {file = "mmh3-4.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:fefef92e9c544a8dbc08f77a8d1b6d48006a750c4375bbcd5ff8199d761e263b"}, + {file = "mmh3-4.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:8e2c1f6a2b41723a4f82bd5a762a777836d29d664fc0095f17910bea0adfd4a6"}, + {file = "mmh3-4.1.0.tar.gz", hash = "sha256:a1cf25348b9acd229dda464a094d6170f47d2850a1fcb762a3b6172d2ce6ca4a"}, +] + +[package.extras] +test = ["mypy (>=1.0)", "pytest (>=7.0.0)"] + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +optional = false +python-versions = "*" +files = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + [[package]] name = "msgpack" version = "1.0.8" @@ -2874,6 +4239,87 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "onnxruntime" +version = "1.18.1" +description = "ONNX Runtime is a runtime accelerator for Machine Learning models" +optional = false +python-versions = "*" +files = [ + {file = "onnxruntime-1.18.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:29ef7683312393d4ba04252f1b287d964bd67d5e6048b94d2da3643986c74d80"}, + {file = "onnxruntime-1.18.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc706eb1df06ddf55776e15a30519fb15dda7697f987a2bbda4962845e3cec05"}, + {file = "onnxruntime-1.18.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7de69f5ced2a263531923fa68bbec52a56e793b802fcd81a03487b5e292bc3a"}, + {file = "onnxruntime-1.18.1-cp310-cp310-win32.whl", hash = "sha256:221e5b16173926e6c7de2cd437764492aa12b6811f45abd37024e7cf2ae5d7e3"}, + {file = "onnxruntime-1.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:75211b619275199c861ee94d317243b8a0fcde6032e5a80e1aa9ded8ab4c6060"}, + {file = "onnxruntime-1.18.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:f26582882f2dc581b809cfa41a125ba71ad9e715738ec6402418df356969774a"}, + {file = "onnxruntime-1.18.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef36f3a8b768506d02be349ac303fd95d92813ba3ba70304d40c3cd5c25d6a4c"}, + {file = "onnxruntime-1.18.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:170e711393e0618efa8ed27b59b9de0ee2383bd2a1f93622a97006a5ad48e434"}, + {file = "onnxruntime-1.18.1-cp311-cp311-win32.whl", hash = "sha256:9b6a33419b6949ea34e0dc009bc4470e550155b6da644571ecace4b198b0d88f"}, + {file = "onnxruntime-1.18.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c1380a9f1b7788da742c759b6a02ba771fe1ce620519b2b07309decbd1a2fe1"}, + {file = "onnxruntime-1.18.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:31bd57a55e3f983b598675dfc7e5d6f0877b70ec9864b3cc3c3e1923d0a01919"}, + {file = "onnxruntime-1.18.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9e03c4ba9f734500691a4d7d5b381cd71ee2f3ce80a1154ac8f7aed99d1ecaa"}, + {file = "onnxruntime-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:781aa9873640f5df24524f96f6070b8c550c66cb6af35710fd9f92a20b4bfbf6"}, + {file = "onnxruntime-1.18.1-cp312-cp312-win32.whl", hash = "sha256:3a2d9ab6254ca62adbb448222e630dc6883210f718065063518c8f93a32432be"}, + {file = "onnxruntime-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:ad93c560b1c38c27c0275ffd15cd7f45b3ad3fc96653c09ce2931179982ff204"}, + {file = "onnxruntime-1.18.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:3b55dc9d3c67626388958a3eb7ad87eb7c70f75cb0f7ff4908d27b8b42f2475c"}, + {file = "onnxruntime-1.18.1-cp38-cp38-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f80dbcfb6763cc0177a31168b29b4bd7662545b99a19e211de8c734b657e0669"}, + {file = "onnxruntime-1.18.1-cp38-cp38-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1ff2c61a16d6c8631796c54139bafea41ee7736077a0fc64ee8ae59432f5c58"}, + {file = "onnxruntime-1.18.1-cp38-cp38-win32.whl", hash = "sha256:219855bd272fe0c667b850bf1a1a5a02499269a70d59c48e6f27f9c8bcb25d02"}, + {file = "onnxruntime-1.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:afdf16aa607eb9a2c60d5ca2d5abf9f448e90c345b6b94c3ed14f4fb7e6a2d07"}, + {file = "onnxruntime-1.18.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:128df253ade673e60cea0955ec9d0e89617443a6d9ce47c2d79eb3f72a3be3de"}, + {file = "onnxruntime-1.18.1-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9839491e77e5c5a175cab3621e184d5a88925ee297ff4c311b68897197f4cde9"}, + {file = "onnxruntime-1.18.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad3187c1faff3ac15f7f0e7373ef4788c582cafa655a80fdbb33eaec88976c66"}, + {file = "onnxruntime-1.18.1-cp39-cp39-win32.whl", hash = "sha256:34657c78aa4e0b5145f9188b550ded3af626651b15017bf43d280d7e23dbf195"}, + {file = "onnxruntime-1.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:9c14fd97c3ddfa97da5feef595e2c73f14c2d0ec1d4ecbea99c8d96603c89589"}, +] + +[package.dependencies] +coloredlogs = "*" +flatbuffers = "*" +numpy = ">=1.21.6,<2.0" +packaging = "*" +protobuf = "*" +sympy = "*" + +[[package]] +name = "openai" +version = "1.37.0" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.37.0-py3-none-any.whl", hash = "sha256:a903245c0ecf622f2830024acdaa78683c70abb8e9d37a497b851670864c9f73"}, + {file = "openai-1.37.0.tar.gz", hash = "sha256:dc8197fc40ab9d431777b6620d962cc49f4544ffc3011f03ce0a805e6eb54adb"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.7,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + [[package]] name = "opentelemetry-api" version = "1.25.0" @@ -2887,7 +4333,61 @@ files = [ [package.dependencies] deprecated = ">=1.2.6" -importlib-metadata = ">=6.0,<=7.1" +importlib-metadata = ">=6.0,<=7.1" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.25.0" +description = "OpenTelemetry Protobuf encoding" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_common-1.25.0-py3-none-any.whl", hash = "sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.25.0.tar.gz", hash = "sha256:c93f4e30da4eee02bacd1e004eb82ce4da143a2f8e15b987a9f603e0a85407d3"}, +] + +[package.dependencies] +opentelemetry-proto = "1.25.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.25.0" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0-py3-none-any.whl", hash = "sha256:3131028f0c0a155a64c430ca600fd658e8e37043cb13209f0109db5c1a3e4eb4"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0.tar.gz", hash = "sha256:c0b1661415acec5af87625587efa1ccab68b873745ca0ee96b69bb1042087eac"}, +] + +[package.dependencies] +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.25.0" +opentelemetry-proto = "1.25.0" +opentelemetry-sdk = ">=1.25.0,<1.26.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.25.0" +description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_http-1.25.0-py3-none-any.whl", hash = "sha256:2eca686ee11b27acd28198b3ea5e5863a53d1266b91cda47c839d95d5e0541a6"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.25.0.tar.gz", hash = "sha256:9f8723859e37c75183ea7afa73a3542f01d0fd274a5b97487ea24cb683d7d684"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.25.0" +opentelemetry-proto = "1.25.0" +opentelemetry-sdk = ">=1.25.0,<1.26.0" +requests = ">=2.7,<3.0" [[package]] name = "opentelemetry-exporter-prometheus" @@ -2963,6 +4463,20 @@ opentelemetry-util-http = "0.46b0" [package.extras] instruments = ["fastapi (>=0.58,<1.0)"] +[[package]] +name = "opentelemetry-proto" +version = "1.25.0" +description = "OpenTelemetry Python Proto" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_proto-1.25.0-py3-none-any.whl", hash = "sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f"}, + {file = "opentelemetry_proto-1.25.0.tar.gz", hash = "sha256:35b6ef9dc4a9f7853ecc5006738ad40443701e52c26099e197895cbda8b815a3"}, +] + +[package.dependencies] +protobuf = ">=3.19,<5.0" + [[package]] name = "opentelemetry-sdk" version = "1.25.0" @@ -3064,6 +4578,17 @@ files = [ {file = "orjson-3.10.0.tar.gz", hash = "sha256:ba4d8cac5f2e2cff36bea6b6481cdb92b38c202bcec603d6f5ff91960595a1ed"}, ] +[[package]] +name = "overrides" +version = "7.7.0" +description = "A decorator to automatically detect mismatch when overriding a method." +optional = false +python-versions = ">=3.6" +files = [ + {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, + {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, +] + [[package]] name = "packaging" version = "24.1" @@ -3163,6 +4688,20 @@ files = [ numpy = ">=1.23.5" types-pytz = ">=2022.1.1" +[[package]] +name = "parameterized" +version = "0.9.0" +description = "Parameterized testing with any Python test framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b"}, + {file = "parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1"}, +] + +[package.extras] +dev = ["jinja2"] + [[package]] name = "parso" version = "0.8.4" @@ -3337,6 +4876,48 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "portalocker" +version = "2.10.1" +description = "Wraps the portalocker recipe for easy usage" +optional = false +python-versions = ">=3.8" +files = [ + {file = "portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf"}, + {file = "portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f"}, +] + +[package.dependencies] +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)", "types-redis"] + +[[package]] +name = "posthog" +version = "3.5.0" +description = "Integrate PostHog into any python application." +optional = false +python-versions = "*" +files = [ + {file = "posthog-3.5.0-py2.py3-none-any.whl", hash = "sha256:3c672be7ba6f95d555ea207d4486c171d06657eb34b3ce25eb043bfe7b6b5b76"}, + {file = "posthog-3.5.0.tar.gz", hash = "sha256:8f7e3b2c6e8714d0c0c542a2109b83a7549f63b7113a133ab2763a89245ef2ef"}, +] + +[package.dependencies] +backoff = ">=1.10.0" +monotonic = ">=1.5" +python-dateutil = ">2.1" +requests = ">=2.7,<3.0" +six = ">=1.5" + +[package.extras] +dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] +sentry = ["django", "sentry-sdk"] +test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] + [[package]] name = "pre-commit" version = "3.8.0" @@ -3383,6 +4964,43 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "proto-plus" +version = "1.24.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, + {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<6.0.0dev" + +[package.extras] +testing = ["google-api-core (>=1.31.5)"] + +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + [[package]] name = "psutil" version = "6.0.0" @@ -3423,6 +5041,53 @@ files = [ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +[[package]] +name = "pulsar-client" +version = "3.5.0" +description = "Apache Pulsar Python client library" +optional = false +python-versions = "*" +files = [ + {file = "pulsar_client-3.5.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:c18552edb2f785de85280fe624bc507467152bff810fc81d7660fa2dfa861f38"}, + {file = "pulsar_client-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18d438e456c146f01be41ef146f649dedc8f7bc714d9eaef94cff2e34099812b"}, + {file = "pulsar_client-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18a26a0719841103c7a89eb1492c4a8fedf89adaa386375baecbb4fa2707e88f"}, + {file = "pulsar_client-3.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ab0e1605dc5f44a126163fd06cd0a768494ad05123f6e0de89a2c71d6e2d2319"}, + {file = "pulsar_client-3.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdef720891b97656fdce3bf5913ea7729b2156b84ba64314f432c1e72c6117fa"}, + {file = "pulsar_client-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:a42544e38773191fe550644a90e8050579476bb2dcf17ac69a4aed62a6cb70e7"}, + {file = "pulsar_client-3.5.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:fd94432ea5d398ea78f8f2e09a217ec5058d26330c137a22690478c031e116da"}, + {file = "pulsar_client-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6252ae462e07ece4071213fdd9c76eab82ca522a749f2dc678037d4cbacd40b"}, + {file = "pulsar_client-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b4d440b2d74323784328b082872ee2f206c440b5d224d7941eb3c083ec06c6"}, + {file = "pulsar_client-3.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f60af840b8d64a2fac5a0c1ce6ae0ddffec5f42267c6ded2c5e74bad8345f2a1"}, + {file = "pulsar_client-3.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2277a447c3b7f6571cb1eb9fc5c25da3fdd43d0b2fb91cf52054adfadc7d6842"}, + {file = "pulsar_client-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:f20f3e9dd50db2a37059abccad42078b7a4754b8bc1d3ae6502e71c1ad2209f0"}, + {file = "pulsar_client-3.5.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:d61f663d85308e12f44033ba95af88730f581a7e8da44f7a5c080a3aaea4878d"}, + {file = "pulsar_client-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1ba0be25b6f747bcb28102b7d906ec1de48dc9f1a2d9eacdcc6f44ab2c9e17"}, + {file = "pulsar_client-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a181e3e60ac39df72ccb3c415d7aeac61ad0286497a6e02739a560d5af28393a"}, + {file = "pulsar_client-3.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3c72895ff7f51347e4f78b0375b2213fa70dd4790bbb78177b4002846f1fd290"}, + {file = "pulsar_client-3.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:547dba1b185a17eba915e51d0a3aca27c80747b6187e5cd7a71a3ca33921decc"}, + {file = "pulsar_client-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:443b786eed96bc86d2297a6a42e79f39d1abf217ec603e0bd303f3488c0234af"}, + {file = "pulsar_client-3.5.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:15b58f5d759dd6166db8a2d90ed05a38063b05cda76c36d190d86ef5c9249397"}, + {file = "pulsar_client-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af34bfe813dddf772a8a298117fa0a036ee963595d8bc8f00d969a0329ae6ed9"}, + {file = "pulsar_client-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27a0fec1dd74e1367d3742ce16679c1807994df60f5e666f440cf39323938fad"}, + {file = "pulsar_client-3.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbcd26ef9c03f96fb9cd91baec3bbd3c4b997834eb3556670d31f41cc25b5f64"}, + {file = "pulsar_client-3.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:afea1d0b6e793fd56e56463145751ff3aa79fdcd5b26e90d0da802a1bbabe07e"}, + {file = "pulsar_client-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:da1ab2fb1bef64b966e9403a0a186ebc90368d99e054ce2cae5b1128478f4ef4"}, + {file = "pulsar_client-3.5.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:9ad5dcc0eb8d2a7c0fb8e1fa146a0c6d4bdaf934f1169080b2c64b2f0573e086"}, + {file = "pulsar_client-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5870c6805b1a57962ed908d1173e97e13470415998393925c86a43694420389"}, + {file = "pulsar_client-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29cb5fedb969895b78301dc00a979133e69940812b8332e4de948bb0ad3db7cb"}, + {file = "pulsar_client-3.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e53c74bfa59b20c66adea95023169060f5048dd8d843e6ef9cd3b8ee2d23e93b"}, + {file = "pulsar_client-3.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99dbadb13967f1add57010971ed36b5a77d24afcdaea01960d0e55e56cf4ba6f"}, + {file = "pulsar_client-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:058887661d438796f42307dcc8054c84dea88a37683dae36498b95d7e1c39b37"}, +] + +[package.dependencies] +certifi = "*" + +[package.extras] +all = ["apache-bookkeeper-client (>=4.16.1)", "fastavro (>=1.9.2)", "grpcio (>=1.60.0)", "prometheus-client", "protobuf (>=3.6.1,<=3.20.3)", "ratelimit"] +avro = ["fastavro (>=1.9.2)"] +functions = ["apache-bookkeeper-client (>=4.16.1)", "grpcio (>=1.60.0)", "prometheus-client", "protobuf (>=3.6.1,<=3.20.3)", "ratelimit"] + [[package]] name = "pure-eval" version = "0.2.3" @@ -3437,6 +5102,57 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "pyarrow" +version = "17.0.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, + {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, + {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, + {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"}, + {file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, + {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, + {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, +] + +[package.dependencies] +numpy = ">=1.16.6" + +[package.extras] +test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] + [[package]] name = "pyasn1" version = "0.6.0" @@ -3448,6 +5164,20 @@ files = [ {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, ] +[[package]] +name = "pyasn1-modules" +version = "0.4.0" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, + {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.7.0" + [[package]] name = "pycparser" version = "2.22" @@ -3658,15 +5388,57 @@ files = [ {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, ] +[[package]] +name = "pypika" +version = "0.48.9" +description = "A SQL query builder API for Python" +optional = false +python-versions = "*" +files = [ + {file = "PyPika-0.48.9.tar.gz", hash = "sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378"}, +] + +[[package]] +name = "pyproject-hooks" +version = "1.1.0" +description = "Wrappers to call pyproject.toml-based build backend hooks." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2"}, + {file = "pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965"}, +] + +[[package]] +name = "pyreadline3" +version = "3.4.1" +description = "A python implementation of GNU readline." +optional = false +python-versions = "*" +files = [ + {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, + {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, +] + +[[package]] +name = "pysbd" +version = "0.3.4" +description = "pysbd (Python Sentence Boundary Disambiguation) is a rule-based sentence boundary detection that works out-of-the-box across many languages." +optional = false +python-versions = ">=3" +files = [ + {file = "pysbd-0.3.4-py3-none-any.whl", hash = "sha256:cd838939b7b0b185fcf86b0baf6636667dfb6e474743beeff878e9f42e022953"}, +] + [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, + {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, ] [package.dependencies] @@ -4114,6 +5886,135 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +[[package]] +name = "qdrant-client" +version = "1.10.1" +description = "Client library for the Qdrant vector search engine" +optional = false +python-versions = ">=3.8" +files = [ + {file = "qdrant_client-1.10.1-py3-none-any.whl", hash = "sha256:b9fb8fe50dd168d92b2998be7c6135d5a229b3a3258ad158cc69c8adf9ff1810"}, + {file = "qdrant_client-1.10.1.tar.gz", hash = "sha256:2284c8c5bb1defb0d9dbacb07d16f344972f395f4f2ed062318476a7951fd84c"}, +] + +[package.dependencies] +grpcio = ">=1.41.0" +grpcio-tools = ">=1.41.0" +httpx = {version = ">=0.20.0", extras = ["http2"]} +numpy = [ + {version = ">=1.21", markers = "python_version >= \"3.8\" and python_version < \"3.12\""}, + {version = ">=1.26", markers = "python_version >= \"3.12\""}, +] +portalocker = ">=2.7.0,<3.0.0" +pydantic = ">=1.10.8" +urllib3 = ">=1.26.14,<3" + +[package.extras] +fastembed = ["fastembed (==0.2.7)"] +fastembed-gpu = ["fastembed-gpu (==0.2.7)"] + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -4135,6 +6036,24 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +description = "OAuthlib authentication support for Requests." +optional = false +python-versions = ">=3.4" +files = [ + {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, + {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + [[package]] name = "respx" version = "0.21.1" @@ -4207,6 +6126,34 @@ files = [ {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, ] +[[package]] +name = "s3transfer" +version = "0.10.2" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">=3.8" +files = [ + {file = "s3transfer-0.10.2-py3-none-any.whl", hash = "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69"}, + {file = "s3transfer-0.10.2.tar.gz", hash = "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + +[[package]] +name = "schema" +version = "0.7.7" +description = "Simple data validation library" +optional = false +python-versions = "*" +files = [ + {file = "schema-0.7.7-py2.py3-none-any.whl", hash = "sha256:5d976a5b50f36e74e2157b47097b60002bd4d42e65425fcc9c9befadb4255dde"}, + {file = "schema-0.7.7.tar.gz", hash = "sha256:7da553abd2958a19dc2547c388cde53398b39196175a9be59ea1caf5ab0a1807"}, +] + [[package]] name = "sentry-sdk" version = "2.12.0" @@ -4261,13 +6208,13 @@ tornado = ["tornado (>=6)"] [[package]] name = "setuptools" -version = "72.1.0" +version = "71.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, + {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, ] [package.extras] @@ -4275,6 +6222,58 @@ core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.te doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "shapely" +version = "2.0.5" +description = "Manipulation and analysis of geometric objects" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shapely-2.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89d34787c44f77a7d37d55ae821f3a784fa33592b9d217a45053a93ade899375"}, + {file = "shapely-2.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:798090b426142df2c5258779c1d8d5734ec6942f778dab6c6c30cfe7f3bf64ff"}, + {file = "shapely-2.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45211276900c4790d6bfc6105cbf1030742da67594ea4161a9ce6812a6721e68"}, + {file = "shapely-2.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e119444bc27ca33e786772b81760f2028d930ac55dafe9bc50ef538b794a8e1"}, + {file = "shapely-2.0.5-cp310-cp310-win32.whl", hash = "sha256:9a4492a2b2ccbeaebf181e7310d2dfff4fdd505aef59d6cb0f217607cb042fb3"}, + {file = "shapely-2.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5cb5ee72f1bc7ace737c9ecd30dc174a5295fae412972d3879bac2e82c8fae"}, + {file = "shapely-2.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bbfb048a74cf273db9091ff3155d373020852805a37dfc846ab71dde4be93ec"}, + {file = "shapely-2.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93be600cbe2fbaa86c8eb70656369f2f7104cd231f0d6585c7d0aa555d6878b8"}, + {file = "shapely-2.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8e71bb9a46814019f6644c4e2560a09d44b80100e46e371578f35eaaa9da1c"}, + {file = "shapely-2.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5251c28a29012e92de01d2e84f11637eb1d48184ee8f22e2df6c8c578d26760"}, + {file = "shapely-2.0.5-cp311-cp311-win32.whl", hash = "sha256:35110e80070d664781ec7955c7de557456b25727a0257b354830abb759bf8311"}, + {file = "shapely-2.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c6b78c0007a34ce7144f98b7418800e0a6a5d9a762f2244b00ea560525290c9"}, + {file = "shapely-2.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:03bd7b5fa5deb44795cc0a503999d10ae9d8a22df54ae8d4a4cd2e8a93466195"}, + {file = "shapely-2.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ff9521991ed9e201c2e923da014e766c1aa04771bc93e6fe97c27dcf0d40ace"}, + {file = "shapely-2.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b65365cfbf657604e50d15161ffcc68de5cdb22a601bbf7823540ab4918a98d"}, + {file = "shapely-2.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21f64e647a025b61b19585d2247137b3a38a35314ea68c66aaf507a1c03ef6fe"}, + {file = "shapely-2.0.5-cp312-cp312-win32.whl", hash = "sha256:3ac7dc1350700c139c956b03d9c3df49a5b34aaf91d024d1510a09717ea39199"}, + {file = "shapely-2.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:30e8737983c9d954cd17feb49eb169f02f1da49e24e5171122cf2c2b62d65c95"}, + {file = "shapely-2.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ff7731fea5face9ec08a861ed351734a79475631b7540ceb0b66fb9732a5f529"}, + {file = "shapely-2.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff9e520af0c5a578e174bca3c18713cd47a6c6a15b6cf1f50ac17dc8bb8db6a2"}, + {file = "shapely-2.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b299b91557b04acb75e9732645428470825061f871a2edc36b9417d66c1fc5"}, + {file = "shapely-2.0.5-cp37-cp37m-win32.whl", hash = "sha256:b5870633f8e684bf6d1ae4df527ddcb6f3895f7b12bced5c13266ac04f47d231"}, + {file = "shapely-2.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:401cb794c5067598f50518e5a997e270cd7642c4992645479b915c503866abed"}, + {file = "shapely-2.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e91ee179af539100eb520281ba5394919067c6b51824e6ab132ad4b3b3e76dd0"}, + {file = "shapely-2.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8af6f7260f809c0862741ad08b1b89cb60c130ae30efab62320bbf4ee9cc71fa"}, + {file = "shapely-2.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5456dd522800306ba3faef77c5ba847ec30a0bd73ab087a25e0acdd4db2514f"}, + {file = "shapely-2.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b714a840402cde66fd7b663bb08cacb7211fa4412ea2a209688f671e0d0631fd"}, + {file = "shapely-2.0.5-cp38-cp38-win32.whl", hash = "sha256:7e8cf5c252fac1ea51b3162be2ec3faddedc82c256a1160fc0e8ddbec81b06d2"}, + {file = "shapely-2.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4461509afdb15051e73ab178fae79974387f39c47ab635a7330d7fee02c68a3f"}, + {file = "shapely-2.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7545a39c55cad1562be302d74c74586f79e07b592df8ada56b79a209731c0219"}, + {file = "shapely-2.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c83a36f12ec8dee2066946d98d4d841ab6512a6ed7eb742e026a64854019b5f"}, + {file = "shapely-2.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89e640c2cd37378480caf2eeda9a51be64201f01f786d127e78eaeff091ec897"}, + {file = "shapely-2.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06efe39beafde3a18a21dde169d32f315c57da962826a6d7d22630025200c5e6"}, + {file = "shapely-2.0.5-cp39-cp39-win32.whl", hash = "sha256:8203a8b2d44dcb366becbc8c3d553670320e4acf0616c39e218c9561dd738d92"}, + {file = "shapely-2.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:7fed9dbfbcfec2682d9a047b9699db8dcc890dfca857ecba872c42185fc9e64e"}, + {file = "shapely-2.0.5.tar.gz", hash = "sha256:bff2366bc786bfa6cb353d6b47d0443c570c32776612e527ee47b6df63fcfe32"}, +] + +[package.dependencies] +numpy = ">=1.14,<3" + +[package.extras] +docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] +test = ["pytest", "pytest-cov"] + [[package]] name = "shellingham" version = "1.5.4" @@ -4308,6 +6307,30 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "spider-client" +version = "0.0.27" +description = "Python SDK for Spider Cloud API" +optional = false +python-versions = "*" +files = [ + {file = "spider-client-0.0.27.tar.gz", hash = "sha256:c3feaf5c491bd9a6c509efa0c8789452497073d9f68e70fc90e7626a6a8365aa"}, +] + +[package.dependencies] +requests = "*" + [[package]] name = "sqlalchemy" version = "2.0.32" @@ -4446,6 +6469,37 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +[[package]] +name = "sympy" +version = "1.13.1" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, + {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, +] + +[package.dependencies] +mpmath = ">=1.1.0,<1.4" + +[package.extras] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] + +[[package]] +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + [[package]] name = "tenacity" version = "8.5.0" @@ -4475,6 +6529,203 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] +[[package]] +name = "tiktoken" +version = "0.7.0" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5"}, + {file = "tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89"}, + {file = "tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97"}, + {file = "tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2398fecd38c921bcd68418675a6d155fad5f5e14c2e92fcf5fe566fa5485a858"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f5f6afb52fb8a7ea1c811e435e4188f2bef81b5e0f7a8635cc79b0eef0193d6"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:861f9ee616766d736be4147abac500732b505bf7013cfaf019b85892637f235e"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54031f95c6939f6b78122c0aa03a93273a96365103793a22e1793ee86da31685"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fffdcb319b614cf14f04d02a52e26b1d1ae14a570f90e9b55461a72672f7b13d"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c72baaeaefa03ff9ba9688624143c858d1f6b755bb85d456d59e529e17234769"}, + {file = "tiktoken-0.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:131b8aeb043a8f112aad9f46011dced25d62629091e51d9dc1adbf4a1cc6aa98"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cabc6dc77460df44ec5b879e68692c63551ae4fae7460dd4ff17181df75f1db7"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8d57f29171255f74c0aeacd0651e29aa47dff6f070cb9f35ebc14c82278f3b25"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ee92776fdbb3efa02a83f968c19d4997a55c8e9ce7be821ceee04a1d1ee149c"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e215292e99cb41fbc96988ef62ea63bb0ce1e15f2c147a61acc319f8b4cbe5bf"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a81bac94769cab437dd3ab0b8a4bc4e0f9cf6835bcaa88de71f39af1791727a"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d6d73ea93e91d5ca771256dfc9d1d29f5a554b83821a1dc0891987636e0ae226"}, + {file = "tiktoken-0.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:2bcb28ddf79ffa424f171dfeef9a4daff61a94c631ca6813f43967cb263b83b9"}, + {file = "tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + +[[package]] +name = "together" +version = "1.2.2" +description = "Python client for Together's Cloud Platform!" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "together-1.2.2-py3-none-any.whl", hash = "sha256:7ce89f902dbaca67e46e693d90182514494f510f3bc16cb89d816a5031ab0433"}, + {file = "together-1.2.2.tar.gz", hash = "sha256:fd026f4a604e1fb3ee2fa5803f31e5e36ad31b3d182ef47f611326de66907d13"}, +] + +[package.dependencies] +aiohttp = ">=3.9.3,<4.0.0" +click = ">=8.1.7,<9.0.0" +eval-type-backport = ">=0.1.3,<0.3.0" +filelock = ">=3.13.1,<4.0.0" +numpy = [ + {version = ">=1.23.5", markers = "python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +pillow = ">=10.3.0,<11.0.0" +pyarrow = ">=10.0.1" +pydantic = ">=2.6.3,<3.0.0" +requests = ">=2.31.0,<3.0.0" +tabulate = ">=0.9.0,<0.10.0" +tqdm = ">=4.66.2,<5.0.0" +typer = ">=0.9,<0.13" + +[[package]] +name = "tokenizers" +version = "0.19.1" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:952078130b3d101e05ecfc7fc3640282d74ed26bcf691400f872563fca15ac97"}, + {file = "tokenizers-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82c8b8063de6c0468f08e82c4e198763e7b97aabfe573fd4cf7b33930ca4df77"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f03727225feaf340ceeb7e00604825addef622d551cbd46b7b775ac834c1e1c4"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:453e4422efdfc9c6b6bf2eae00d5e323f263fff62b29a8c9cd526c5003f3f642"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02e81bf089ebf0e7f4df34fa0207519f07e66d8491d963618252f2e0729e0b46"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b07c538ba956843833fee1190cf769c60dc62e1cf934ed50d77d5502194d63b1"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28cab1582e0eec38b1f38c1c1fb2e56bce5dc180acb1724574fc5f47da2a4fe"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b01afb7193d47439f091cd8f070a1ced347ad0f9144952a30a41836902fe09e"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7fb297edec6c6841ab2e4e8f357209519188e4a59b557ea4fafcf4691d1b4c98"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e8a3dd055e515df7054378dc9d6fa8c8c34e1f32777fb9a01fea81496b3f9d3"}, + {file = "tokenizers-0.19.1-cp310-none-win32.whl", hash = "sha256:7ff898780a155ea053f5d934925f3902be2ed1f4d916461e1a93019cc7250837"}, + {file = "tokenizers-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:bea6f9947e9419c2fda21ae6c32871e3d398cba549b93f4a65a2d369662d9403"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b"}, + {file = "tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256"}, + {file = "tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c"}, + {file = "tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57"}, + {file = "tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:bb9dfe7dae85bc6119d705a76dc068c062b8b575abe3595e3c6276480e67e3f1"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:1f0360cbea28ea99944ac089c00de7b2e3e1c58f479fb8613b6d8d511ce98267"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71e3ec71f0e78780851fef28c2a9babe20270404c921b756d7c532d280349214"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b82931fa619dbad979c0ee8e54dd5278acc418209cc897e42fac041f5366d626"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ff5b90eabdcdaa19af697885f70fe0b714ce16709cf43d4952f1f85299e73a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e742d76ad84acbdb1a8e4694f915fe59ff6edc381c97d6dfdd054954e3478ad4"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8c5d59d7b59885eab559d5bc082b2985555a54cda04dda4c65528d90ad252ad"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b2da5c32ed869bebd990c9420df49813709e953674c0722ff471a116d97b22d"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:638e43936cc8b2cbb9f9d8dde0fe5e7e30766a3318d2342999ae27f68fdc9bd6"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:78e769eb3b2c79687d9cb0f89ef77223e8e279b75c0a968e637ca7043a84463f"}, + {file = "tokenizers-0.19.1-cp37-none-win32.whl", hash = "sha256:72791f9bb1ca78e3ae525d4782e85272c63faaef9940d92142aa3eb79f3407a3"}, + {file = "tokenizers-0.19.1-cp37-none-win_amd64.whl", hash = "sha256:f3bbb7a0c5fcb692950b041ae11067ac54826204318922da754f908d95619fbc"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:07f9295349bbbcedae8cefdbcfa7f686aa420be8aca5d4f7d1ae6016c128c0c5"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10a707cc6c4b6b183ec5dbfc5c34f3064e18cf62b4a938cb41699e33a99e03c1"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6309271f57b397aa0aff0cbbe632ca9d70430839ca3178bf0f06f825924eca22"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad23d37d68cf00d54af184586d79b84075ada495e7c5c0f601f051b162112dc"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:427c4f0f3df9109314d4f75b8d1f65d9477033e67ffaec4bca53293d3aca286d"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e83a31c9cf181a0a3ef0abad2b5f6b43399faf5da7e696196ddd110d332519ee"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c27b99889bd58b7e301468c0838c5ed75e60c66df0d4db80c08f43462f82e0d3"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bac0b0eb952412b0b196ca7a40e7dce4ed6f6926489313414010f2e6b9ec2adf"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a6298bde623725ca31c9035a04bf2ef63208d266acd2bed8c2cb7d2b7d53ce6"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:08a44864e42fa6d7d76d7be4bec62c9982f6f6248b4aa42f7302aa01e0abfd26"}, + {file = "tokenizers-0.19.1-cp38-none-win32.whl", hash = "sha256:1de5bc8652252d9357a666e609cb1453d4f8e160eb1fb2830ee369dd658e8975"}, + {file = "tokenizers-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:0bcce02bf1ad9882345b34d5bd25ed4949a480cf0e656bbd468f4d8986f7a3f1"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0b9394bd204842a2a1fd37fe29935353742be4a3460b6ccbaefa93f58a8df43d"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4692ab92f91b87769d950ca14dbb61f8a9ef36a62f94bad6c82cc84a51f76f6a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6258c2ef6f06259f70a682491c78561d492e885adeaf9f64f5389f78aa49a051"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85cf76561fbd01e0d9ea2d1cbe711a65400092bc52b5242b16cfd22e51f0c58"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670b802d4d82bbbb832ddb0d41df7015b3e549714c0e77f9bed3e74d42400fbe"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85aa3ab4b03d5e99fdd31660872249df5e855334b6c333e0bc13032ff4469c4a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf001afbbed111a79ca47d75941e9e5361297a87d186cbfc11ed45e30b5daba"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c89aa46c269e4e70c4d4f9d6bc644fcc39bb409cb2a81227923404dd6f5227"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:39c1ec76ea1027438fafe16ecb0fb84795e62e9d643444c1090179e63808c69d"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c2a0d47a89b48d7daa241e004e71fb5a50533718897a4cd6235cb846d511a478"}, + {file = "tokenizers-0.19.1-cp39-none-win32.whl", hash = "sha256:61b7fe8886f2e104d4caf9218b157b106207e0f2a4905c9c7ac98890688aabeb"}, + {file = "tokenizers-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:f97660f6c43efd3e0bfd3f2e3e5615bf215680bad6ee3d469df6454b8c6e8256"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b11853f17b54c2fe47742c56d8a33bf49ce31caf531e87ac0d7d13d327c9334"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d26194ef6c13302f446d39972aaa36a1dda6450bc8949f5eb4c27f51191375bd"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e8d1ed93beda54bbd6131a2cb363a576eac746d5c26ba5b7556bc6f964425594"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca407133536f19bdec44b3da117ef0d12e43f6d4b56ac4c765f37eca501c7bda"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce05fde79d2bc2e46ac08aacbc142bead21614d937aac950be88dc79f9db9022"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:35583cd46d16f07c054efd18b5d46af4a2f070a2dd0a47914e66f3ff5efb2b1e"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:43350270bfc16b06ad3f6f07eab21f089adb835544417afda0f83256a8bf8b75"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4399b59d1af5645bcee2072a463318114c39b8547437a7c2d6a186a1b5a0e2d"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6852c5b2a853b8b0ddc5993cd4f33bfffdca4fcc5d52f89dd4b8eada99379285"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd266ae85c3d39df2f7e7d0e07f6c41a55e9a3123bb11f854412952deacd828"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecb2651956eea2aa0a2d099434134b1b68f1c31f9a5084d6d53f08ed43d45ff2"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b279ab506ec4445166ac476fb4d3cc383accde1ea152998509a94d82547c8e2a"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89183e55fb86e61d848ff83753f64cded119f5d6e1f553d14ffee3700d0a4a49"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2edbc75744235eea94d595a8b70fe279dd42f3296f76d5a86dde1d46e35f574"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0e64bfde9a723274e9a71630c3e9494ed7b4c0f76a1faacf7fe294cd26f7ae7c"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b5ca92bfa717759c052e345770792d02d1f43b06f9e790ca0a1db62838816f3"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f8a20266e695ec9d7a946a019c1d5ca4eddb6613d4f466888eee04f16eedb85"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63c38f45d8f2a2ec0f3a20073cccb335b9f99f73b3c69483cd52ebc75369d8a1"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dd26e3afe8a7b61422df3176e06664503d3f5973b94f45d5c45987e1cb711876"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:eddd5783a4a6309ce23432353cdb36220e25cbb779bfa9122320666508b44b88"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:56ae39d4036b753994476a1b935584071093b55c7a72e3b8288e68c313ca26e7"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9939ca7e58c2758c01b40324a59c034ce0cebad18e0d4563a9b1beab3018243"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c330c0eb815d212893c67a032e9dc1b38a803eccb32f3e8172c19cc69fbb439"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec11802450a2487cdf0e634b750a04cbdc1c4d066b97d94ce7dd2cb51ebb325b"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b718f316b596f36e1dae097a7d5b91fc5b85e90bf08b01ff139bd8953b25af"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ed69af290c2b65169f0ba9034d1dc39a5db9459b32f1dd8b5f3f32a3fcf06eab"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f8a9c828277133af13f3859d1b6bf1c3cb6e9e1637df0e45312e6b7c2e622b1f"}, + {file = "tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + [[package]] name = "tomli" version = "2.0.1" @@ -4506,6 +6757,26 @@ files = [ {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, ] +[[package]] +name = "tqdm" +version = "4.66.4" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "traitlets" version = "5.14.3" @@ -4769,13 +7040,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.30.5" +version = "0.30.3" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.30.5-py3-none-any.whl", hash = "sha256:b2d86de274726e9878188fa07576c9ceeff90a839e2b6e25c917fe05f5a6c835"}, - {file = "uvicorn-0.30.5.tar.gz", hash = "sha256:ac6fdbd4425c5fd17a9fe39daf4d4d075da6fdc80f653e5894cdc2fd98752bee"}, + {file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"}, + {file = "uvicorn-0.30.3.tar.gz", hash = "sha256:0d114d0831ff1adbf231d358cbf42f17333413042552a624ea6a9b4c33dcfd81"}, ] [package.dependencies] @@ -4981,6 +7252,22 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + [[package]] name = "websockets" version = "12.0" @@ -5367,4 +7654,4 @@ local = [] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "877648288f4f9d5d3304c36853d1b2c5f833d673665bf9b1c707c37bbf02e6ad" +content-hash = "747dad35b9e5b1338a989ea6bfd4ac3465ba34f792639aeabda3c1ca9b40c689" diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index 6182d84b4aae..d98c673e0767 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -77,6 +77,10 @@ setuptools = ">=70" nanoid = "^2.0.0" filelock = "^3.15.4" grandalf = "^0.8.0" +crewai = "^0.36.0" +spider-client = "^0.0.27" + + [tool.poetry.extras] deploy = ["celery", "redis", "flower"] local = ["llama-cpp-python", "sentence-transformers", "ctransformers"] diff --git a/src/frontend/src/icons/Spider/SpiderIcon.jsx b/src/frontend/src/icons/Spider/SpiderIcon.jsx new file mode 100644 index 000000000000..1d10d9fee820 --- /dev/null +++ b/src/frontend/src/icons/Spider/SpiderIcon.jsx @@ -0,0 +1,18 @@ +const SvgSpiderIcon = (props) => ( + + Spider v0 Logo + + +); +export default SvgSpiderIcon; diff --git a/src/frontend/src/icons/Spider/index.tsx b/src/frontend/src/icons/Spider/index.tsx new file mode 100644 index 000000000000..65e41c36412c --- /dev/null +++ b/src/frontend/src/icons/Spider/index.tsx @@ -0,0 +1,9 @@ +import React, { forwardRef } from "react"; +import SvgSpiderIcon from "./SpiderIcon"; + +export const SpiderIcon = forwardRef< + SVGSVGElement, + React.PropsWithChildren<{}> +>((props, ref) => { + return ; +}); diff --git a/src/frontend/src/icons/Spider/spider_logo.svg b/src/frontend/src/icons/Spider/spider_logo.svg new file mode 100644 index 000000000000..604a09d01d74 --- /dev/null +++ b/src/frontend/src/icons/Spider/spider_logo.svg @@ -0,0 +1 @@ +Spider v1 Logo diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index eb37edf578eb..553439403f28 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -211,6 +211,7 @@ import { SearxIcon } from "../icons/Searx"; import { ShareIcon } from "../icons/Share"; import { Share2Icon } from "../icons/Share2"; import SvgSlackIcon from "../icons/Slack/SlackIcon"; +import { SpiderIcon } from "../icons/Spider"; import { Streamlit } from "../icons/Streamlit"; import { UpstashSvgIcon } from "../icons/Upstash"; import { VectaraIcon } from "../icons/VectaraIcon"; @@ -412,6 +413,7 @@ export const nodeIconsLucide: iconsType = { Weaviate: WeaviateIcon, Searx: SearxIcon, SlackDirectoryLoader: SvgSlackIcon, + SpiderTool: SpiderIcon, SupabaseVectorStore: SupabaseIcon, Supabase: SupabaseIcon, VertexAI: VertexAIIcon, From 4d1c8ad92a51bc0c89afc8aa73d1cb4216ea9493 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Aug 2024 12:04:36 -0300 Subject: [PATCH 02/38] ci: add version check in workflow to skip jobs for unreleased versions of langflow-base (#3244) feat: add version check in workflow to skip jobs for unreleased versions of langflow-base --- .github/workflows/python_test.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/python_test.yml b/.github/workflows/python_test.yml index ba4b2b892a64..5f6671a024ad 100644 --- a/.github/workflows/python_test.yml +++ b/.github/workflows/python_test.yml @@ -69,14 +69,30 @@ jobs: python-version: ${{ matrix.python-version }} poetry-version: ${{ env.POETRY_VERSION }} cache-key: ${{ runner.os }}-poetry-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }} + - name: Check Version + id: check-version + run: | + version=$(cd src/backend/base && poetry version --short) + last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1) + if [ "$version" != "$last_released_version" ]; then + echo "Version $version has not been released yet. Skipping the rest of the job." + echo skipped=true >> $GITHUB_OUTPUT + exit 0 + else + echo version=$version >> $GITHUB_OUTPUT + echo skipped=false >> $GITHUB_OUTPUT + fi - name: Build wheel + if: steps.check-version.outputs.skipped == 'false' run: | poetry env use ${{ matrix.python-version }} make build main=true - name: Install wheel + if: steps.check-version.outputs.skipped == 'false' run: | python -m pip install dist/*.whl - name: Test CLI + if: steps.check-version.outputs.skipped == 'false' run: | python -m langflow run --host 127.0.0.1 --port 7860 --backend-only & SERVER_PID=$! From 881828c4a40295d44a4674fae7c6429bae29c07a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Aug 2024 12:04:50 -0300 Subject: [PATCH 03/38] ci: refactor release workflow and Docker build process (#3245) * feat: update docker-build.yml to conditionally retrieve version and adjust tagging logic for Docker images in workflows * Refactor release workflow to separate base and main package handling - Split `release_package` input into `release_package_base` and `release_package_main` - Add new inputs for building Docker images: `build_docker_base` and `build_docker_main` - Update conditional checks and job dependencies to reflect new inputs - Separate Docker build workflows for base and main packages * Refactor release.yml to introduce separate inputs for base and main packages, enhancing workflow flexibility and clarity * chore: update release.yml to set default pre-release option to false, reflecting new workflow strategy * chore: add pre-release check to release.yml to validate version format before proceeding with the workflow * chore: remove deprecated pre-release workflows, consolidating configuration for cleaner CI/CD process * chore: modify pre-release check in release.yml to use poetry version for validation, enhancing version format accuracy * chore: refine pre-release version check in release.yml for improved regex validation, ensuring accurate version detection --- .github/workflows/docker-build.yml | 41 +++++-- .github/workflows/pre-release-base.yml | 80 ------------- .github/workflows/pre-release-langflow.yml | 133 --------------------- .github/workflows/pre-release.yml | 129 -------------------- .github/workflows/release.yml | 62 ++++++++-- 5 files changed, 85 insertions(+), 360 deletions(-) delete mode 100644 .github/workflows/pre-release-base.yml delete mode 100644 .github/workflows/pre-release-langflow.yml delete mode 100644 .github/workflows/pre-release.yml diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index ae4b4a0395c4..7b425c7e2575 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -3,7 +3,7 @@ on: workflow_call: inputs: version: - required: true + required: false type: string release_type: required: true @@ -33,8 +33,33 @@ env: TEST_TAG: "langflowai/langflow:test" jobs: + get-version: + name: Get Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-version-input.outputs.version || steps.get-version-base.outputs.version || steps.get-version-main.outputs.version }} + steps: + - name: Get Version from Input + if : ${{ inputs.version != '' }} + id: get-version-input + run: | + version=${{ inputs.version }} + echo version=$version >> $GITHUB_OUTPUT + - name: Get Version Main + if : ${{ inputs.version == '' && inputs.release_type == 'base' }} + id: get-version-base + run: | + version=$(poetry version --short) + echo version=$version >> $GITHUB_OUTPUT + - name: Get Version Base + if : ${{ inputs.version == '' && inputs.release_type == 'main' }} + id: get-version-main + run: | + version=$(cd src/backend/base && poetry version --short) + echo version=$version >> $GITHUB_OUTPUT setup: runs-on: ubuntu-latest + needs: get-version outputs: tags: ${{ steps.set-vars.outputs.tags }} file: ${{ steps.set-vars.outputs.file }} @@ -44,13 +69,13 @@ jobs: id: set-vars run: | if [[ "${{ inputs.release_type }}" == "base" ]]; then - echo "tags=langflowai/langflow:base-${{ inputs.version }},langflowai/langflow:base-latest" >> $GITHUB_OUTPUT + echo "tags=langflowai/langflow:base-${{ needs.get-version.outputs.version }},langflowai/langflow:base-latest" >> $GITHUB_OUTPUT echo "file=./docker/build_and_push_base.Dockerfile" >> $GITHUB_OUTPUT else if [[ "${{ inputs.pre_release }}" == "true" ]]; then - echo "tags=langflowai/langflow:${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "tags=langflowai/langflow:${{ needs.get-version.outputs.version }}" >> $GITHUB_OUTPUT else - echo "tags=langflowai/langflow:${{ inputs.version }},langflowai/langflow:latest" >> $GITHUB_OUTPUT + echo "tags=langflowai/langflow:${{ needs.get-version.outputs.version }},langflowai/langflow:latest" >> $GITHUB_OUTPUT fi echo "file=./docker/build_and_push.Dockerfile" >> $GITHUB_OUTPUT fi @@ -79,17 +104,17 @@ jobs: build_components: if: ${{ inputs.release_type == 'main' }} runs-on: ubuntu-latest - needs: build + needs: [build, get-version] strategy: matrix: component: [backend, frontend] include: - component: backend dockerfile: ./docker/build_and_push_backend.Dockerfile - tags: ${{ inputs.pre_release == 'true' && format('langflowai/langflow-backend:{0}', inputs.version) || format('langflowai/langflow-backend:{0},langflowai/langflow-backend:latest', inputs.version) }} + tags: ${{ inputs.pre_release == 'true' && format('langflowai/langflow-backend:{0}', needs.get-version.outputs.version) || format('langflowai/langflow-backend:{0},langflowai/langflow-backend:latest', needs.get-version.outputs.version) }} - component: frontend dockerfile: ./docker/frontend/build_and_push_frontend.Dockerfile - tags: ${{ inputs.pre_release == 'true' && format('langflowai/langflow-frontend:{0}', inputs.version) || format('langflowai/langflow-frontend:{0},langflowai/langflow-frontend:latest', inputs.version) }} + tags: ${{ inputs.pre_release == 'true' && format('langflowai/langflow-frontend:{0}', needs.get-version.outputs.version) || format('langflowai/langflow-frontend:{0},langflowai/langflow-frontend:latest', needs.get-version.outputs.version) }} steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx @@ -107,7 +132,7 @@ jobs: context: . push: true build-args: | - LANGFLOW_IMAGE=langflowai/langflow:${{ inputs.version }} + LANGFLOW_IMAGE=langflowai/langflow:${{ needs.get-version.outputs.version }} file: ${{ matrix.dockerfile }} tags: ${{ matrix.tags }} # provenance: false will result in a single manifest for all platforms which makes the image pullable from arm64 machines via the emulation (e.g. Apple Silicon machines) diff --git a/.github/workflows/pre-release-base.yml b/.github/workflows/pre-release-base.yml deleted file mode 100644 index 6d9e2f0bdc38..000000000000 --- a/.github/workflows/pre-release-base.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Langflow Base Pre-release -run-name: Langflow Base Pre-release by @${{ github.actor }} -on: - workflow_dispatch: - inputs: - release_package: - description: "Release package" - required: true - type: boolean - default: false - -env: - POETRY_VERSION: "1.8.2" - -jobs: - release: - name: Release Langflow Base - if: inputs.release_package == true - runs-on: ubuntu-latest - outputs: - version: ${{ steps.check-version.outputs.version }} - steps: - - uses: actions/checkout@v4 - - name: Install poetry - run: pipx install poetry==${{ env.POETRY_VERSION }} - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - cache: "poetry" - - name: Check Version - id: check-version - # In this step, we should check the version of the package - # and see if it is a version that is already released - # echo version=$(cd src/backend/base && poetry version --short) >> $GITHUB_OUTPUT - # cd src/backend/base && poetry version --short should - # be different than the last release version in pypi - # which we can get from curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1 - run: | - version=$(cd src/backend/base && poetry version --short) - last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1) - if [ "$version" = "$last_released_version" ]; then - echo "Version $version is already released. Skipping release." - exit 1 - else - echo version=$version >> $GITHUB_OUTPUT - fi - - name: Build project for distribution - run: make build base=true - - name: Publish to PyPI - env: - POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} - run: | - make publish base=true - docker_build: - name: Build Docker Image - runs-on: ubuntu-latest - needs: release - steps: - - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - id: qemu - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: true - file: ./docker/build_and_push_base.Dockerfile - tags: | - langflowai/langflow:base-${{ needs.release.outputs.version }} - # provenance: false will result in a single manifest for all platforms which makes the image pullable from arm64 machines via the emulation (e.g. Apple Silicon machines) - provenance: false diff --git a/.github/workflows/pre-release-langflow.yml b/.github/workflows/pre-release-langflow.yml deleted file mode 100644 index 5a052dcc84d2..000000000000 --- a/.github/workflows/pre-release-langflow.yml +++ /dev/null @@ -1,133 +0,0 @@ -name: Langflow Pre-release -run-name: Langflow Pre-release by @${{ github.actor }} -on: - workflow_dispatch: - inputs: - release_package: - description: "Release package" - required: true - type: boolean - default: false - workflow_run: - workflows: ["pre-release-base"] - types: [completed] - branches: [dev] - -env: - POETRY_VERSION: "1.8.2" - -jobs: - release: - name: Release Langflow - if: inputs.release_package == true - runs-on: ubuntu-latest - outputs: - version: ${{ steps.check-version.outputs.version }} - steps: - - uses: actions/checkout@v4 - - name: Install poetry - run: pipx install poetry==${{ env.POETRY_VERSION }} - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - cache: "poetry" - - name: Check Version - id: check-version - run: | - version=$(poetry version --short) - last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1) - if [ "$version" = "$last_released_version" ]; then - echo "Version $version is already released. Skipping release." - exit 1 - else - echo version=$version >> $GITHUB_OUTPUT - fi - - name: Build project for distribution - run: make build main=true - - name: Display pyproject.toml langflow-base Version - run: cat pyproject.toml | grep langflow-base - - name: Publish to PyPI - env: - POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} - run: | - make publish main=true - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: dist - path: dist - - docker_build: - name: Build Docker Image - runs-on: ubuntu-latest - needs: release - steps: - - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - id: qemu - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: true - file: ./docker/build_and_push.Dockerfile - # provenance: false will result in a single manifest for all platforms which makes the image pullable from arm64 machines via the emulation (e.g. Apple Silicon machines) - provenance: false - tags: | - langflowai/langflow:${{ needs.release.outputs.version }} - langflowai/langflow:1.0-alpha - - name: Build and push (frontend) - uses: docker/build-push-action@v6 - with: - context: . - push: true - file: ./docker/frontend/build_and_push_frontend.Dockerfile - # provenance: false will result in a single manifest for all platforms which makes the image pullable from arm64 machines via the emulation (e.g. Apple Silicon machines) - provenance: false - tags: | - langflowai/langflow-frontend:${{ needs.release.outputs.version }} - langflowai/langflow-frontend:1.0-alpha - - name: Wait for Docker Hub to propagate - run: sleep 120 - - name: Build and push (backend) - uses: docker/build-push-action@v6 - with: - context: . - push: true - file: ./docker/build_and_push_backend.Dockerfile - # provenance: false will result in a single manifest for all platforms which makes the image pullable from arm64 machines via the emulation (e.g. Apple Silicon machines) - provenance: false - build-args: | - LANGFLOW_IMAGE=langflowai/langflow:${{ needs.release.outputs.version }} - tags: | - langflowai/langflow-backend:${{ needs.release.outputs.version }} - langflowai/langflow-backend:1.0-alpha - - create_release: - name: Create Release - runs-on: ubuntu-latest - needs: [docker_build, release] - steps: - - uses: actions/download-artifact@v4 - with: - name: dist - path: dist - - name: Create Release - uses: ncipollo/release-action@v1 - with: - artifacts: "dist/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - generateReleaseNotes: true - prerelease: true - tag: v${{ needs.release.outputs.version }} - commit: dev diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml deleted file mode 100644 index 814edd7beb61..000000000000 --- a/.github/workflows/pre-release.yml +++ /dev/null @@ -1,129 +0,0 @@ -name: Langflow Pre-release (Unified) -run-name: Langflow (${{inputs.release_type}}) Pre-release by @${{ github.actor }} -on: - workflow_dispatch: - inputs: - release_package: - description: "Release package" - required: true - type: boolean - default: false - release_type: - description: "Type of release (base or main)" - required: true - type: choice - options: - - base - - main - -env: - POETRY_VERSION: "1.8.2" - -jobs: - release: - name: Release Langflow - if: inputs.release_package == true - runs-on: ubuntu-latest - outputs: - version: ${{ steps.check-version.outputs.version }} - steps: - - uses: actions/checkout@v4 - - name: Install poetry - run: pipx install poetry==${{ env.POETRY_VERSION }} - - name: Set up Python 3.10 - uses: actions/setup-python@v5 - with: - python-version: "3.10" - cache: "poetry" - - name: Set up Nodejs 20 - uses: actions/setup-node@v4 - with: - node-version: "20" - - name: Check Version - id: check-version - run: | - if [ "${{ inputs.release_type }}" == "base" ]; then - version=$(cd src/backend/base && poetry version --short) - last_released_version=$(curl -s "https://pypi.org/pypi/langflow-base/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1) - else - version=$(poetry version --short) - last_released_version=$(curl -s "https://pypi.org/pypi/langflow/json" | jq -r '.releases | keys | .[]' | sort -V | tail -n 1) - fi - if [ "$version" = "$last_released_version" ]; then - echo "Version $version is already released. Skipping release." - exit 1 - else - echo version=$version >> $GITHUB_OUTPUT - fi - - name: Build project for distribution - run: | - if [ "${{ inputs.release_type }}" == "base" ]; then - make build base=true - else - make build main=true - fi - - name: Test CLI - run: | - if [ "${{ inputs.release_type }}" == "base" ]; then - python -m pip install src/backend/base/dist/*.whl - else - python -m pip install dist/*.whl - fi - python -m langflow run --host 127.0.0.1 --port 7860 & - SERVER_PID=$! - # Wait for the server to start - timeout 120 bash -c 'until curl -f http://127.0.0.1:7860/health; do sleep 2; done' || (echo "Server did not start in time" && kill $SERVER_PID && exit 1) - # Terminate the server - kill $SERVER_PID || (echo "Failed to terminate the server" && exit 1) - sleep 10 # give the server some time to terminate - # Check if the server is still running - if kill -0 $SERVER_PID 2>/dev/null; then - echo "Failed to terminate the server" - exit 1 - else - echo "Server terminated successfully" - fi - - name: Publish to PyPI - env: - POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} - run: | - if [ "${{ inputs.release_type }}" == "base" ]; then - make publish base=true - else - make publish main=true - fi - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: dist${{ inputs.release_type }} - path: ${{ inputs.release_type == 'base' && 'src/backend/base/dist' || 'dist' }} - - call_docker_build: - name: Call Docker Build Workflow - needs: release - uses: langflow-ai/langflow/.github/workflows/docker-build.yml@dev - with: - version: ${{ needs.release.outputs.version }} - release_type: ${{ inputs.release_type }} - secrets: inherit - - create_release: - name: Create Release - runs-on: ubuntu-latest - needs: [release] - if: ${{ inputs.release_type == 'main' }} - steps: - - uses: actions/download-artifact@v4 - with: - name: dist${{ inputs.release_type }} - path: dist - - name: Create Release - uses: ncipollo/release-action@v1 - with: - artifacts: "dist/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - generateReleaseNotes: true - prerelease: true - tag: v${{ needs.release.outputs.version }} - commit: dev diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb049907e419..57b5e3ed9c22 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,8 +4,23 @@ run-name: Langflow Release by @${{ github.actor }} on: workflow_dispatch: inputs: - release_package: - description: "Release package" + release_package_base: + description: "Release Langflow Base" + required: true + type: boolean + default: false + release_package_main: + description: "Release Langflow" + required: true + type: boolean + default: false + build_docker_base: + description: "Build Docker Image for Langflow Base" + required: true + type: boolean + default: false + build_docker_main: + description: "Build Docker Image for Langflow" required: true type: boolean default: false @@ -13,7 +28,7 @@ on: description: "Pre-release" required: false type: boolean - default: true + default: false env: @@ -27,7 +42,7 @@ jobs: release-base: name: Release Langflow Base needs: [ci] - if: inputs.release_package == true + if: inputs.release_package_base == true runs-on: ubuntu-latest outputs: version: ${{ steps.check-version.outputs.version }} @@ -93,7 +108,7 @@ jobs: release-main: name: Release Langflow Main - if: inputs.release_package == true + if: inputs.release_package_main == true needs: [release-base] runs-on: ubuntu-latest outputs: @@ -111,6 +126,18 @@ jobs: uses: actions/setup-node@v4 with: node-version: "20" + # If pre-release is true, we need to check if ["a", "b", "rc", "dev", "post"] is in the version string + # if the version string is incorrect, we need to exit the workflow + - name: Check if pre-release + if: inputs.pre_release == true + run: | + version=$(poetry version --short) + if [[ "${version}" =~ ^([0-9]+\.)?([0-9]+\.)?[0-9]+((a|b|rc|dev|post)([0-9]+))$ ]]; then + echo "Pre-release version detected. Continuing with the release." + else + echo "Invalid pre-release version detected. Exiting the workflow." + exit 1 + fi - name: Check Version id: check-version run: | @@ -155,19 +182,34 @@ jobs: name: dist-main path: dist - call_docker_build: - name: Call Docker Build Workflow - needs: [release-base, release-main] - uses: langflow-ai/langflow/.github/workflows/docker-build.yml@main + call_docker_build_base: + name: Call Docker Build Workflow for Langflow Base + if : inputs.build_docker_base == true + uses: ./.github/workflows/docker-build.yml strategy: matrix: release_type: - base + with: + # version should be needs.release-base.outputs.version if release_type is base + # version should be needs.release-main.outputs.version if release_type is main + version: '' + release_type: ${{ matrix.release_type }} + pre_release: ${{ inputs.pre_release }} + secrets: inherit + + call_docker_build_main: + name: Call Docker Build Workflow for Langflow + if : inputs.build_docker_main == true + uses: ./.github/workflows/docker-build.yml + strategy: + matrix: + release_type: - main with: # version should be needs.release-base.outputs.version if release_type is base # version should be needs.release-main.outputs.version if release_type is main - version: ${{ matrix.release_type == 'base' && needs.release-base.outputs.version || matrix.release_type == 'main' && needs.release-main.outputs.version }} + version: '' release_type: ${{ matrix.release_type }} pre_release: ${{ inputs.pre_release }} secrets: inherit From ca008a13468e55d8dba0a8050a3ff14e17cc655f Mon Sep 17 00:00:00 2001 From: Daniel Gines Date: Thu, 8 Aug 2024 12:15:41 -0300 Subject: [PATCH 04/38] fix: update PYTHON_REQUIRED extraction to correctly capture Python version (#3199) * Updates and changes to the Makefile: 1. Added removal of `frontend` directory inside `src/backend/base/langflow/` and `build` directory inside `src/frontend/` to the `clean_npm_cache` target. 2. Added descriptive comments for the `build_and_install`, `build_and_run`, `fix_codespell`, `setup_poetry`, `unit_tests`, `integration_tests`, and `tests_frontend` targets. Looking forward to your feedback. * Improvements: Structure and Functionality Improvements - Reorganized commands to facilitate understanding of the structure - Enhanced check_tools to detect the Python version - Added a multi-environment script to support check_tools - make init now builds the frontend and runs the application - Aesthetic improvements in output messages TO-DO: - Reorganize container-related commands - Reorganize other miscellaneous utilities - Document usage in the application docs - Prepare the dev environment following the maintainers' recommended practices * Removed pre-commit as it is no longer used. * Restored 'patch' command in Makefile, it updates the 'pyproject.toml' with the new project version. * fix: update PYTHON_REQUIRED extraction to correctly capture Python version Changed the method of extracting the required Python version from `pyproject.toml`: - Old method: `PYTHON_REQUIRED=$(shell grep "^python" pyproject.toml | sed -n 's/.*"\(.*\)"$$/\1/p')` - New method: `PYTHON_REQUIRED=$(shell grep '^python[[:space:]]*=' pyproject.toml | sed -n 's/.*"\([^"]*\)".*/\1/p')` The old method of capturing the Python version was too broad and could inadvertently match other dependencies starting with "python" (e.g., `python-socketio`, `python-dotenv`). This could lead to incorrect extraction of the required Python version, potentially causing version mismatches and failures in environment setup. * fix: update PYTHON_REQUIRED extraction to correctly capture Python version Changed the method of extracting the required Python version from `pyproject.toml`: - Old method: `PYTHON_REQUIRED=$(shell grep "^python" pyproject.toml | sed -n 's/.*"\(.*\)"$$/\1/p')` - New method: `PYTHON_REQUIRED=$(shell grep '^python[[:space:]]*=' pyproject.toml | sed -n 's/.*"\([^"]*\)".*/\1/p')` The old method of capturing the Python version was too broad and could inadvertently match other dependencies starting with "python" (e.g., `python-socketio`, `python-dotenv`). This could lead to incorrect extraction of the required Python version, potentially causing version mismatches and failures in environment setup. --------- Co-authored-by: Gabriel Luiz Freitas Almeida --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 484fc27bdaa4..6e84e64c926a 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ DOCKERFILE=docker/build_and_push.Dockerfile DOCKERFILE_BACKEND=docker/build_and_push_backend.Dockerfile DOCKERFILE_FRONTEND=docker/frontend/build_and_push_frontend.Dockerfile DOCKER_COMPOSE=docker_example/docker-compose.yml -PYTHON_REQUIRED=$(shell grep "^python" pyproject.toml | sed -n 's/.*"\(.*\)"$$/\1/p') +PYTHON_REQUIRED=$(shell grep '^python[[:space:]]*=' pyproject.toml | sed -n 's/.*"\([^"]*\)".*/\1/p') RED=\033[0;31m NC=\033[0m # No Color GREEN=\033[0;32m From 3e6c863a8b596eb812335bec993861a9fb4065b5 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Thu, 8 Aug 2024 14:53:21 -0400 Subject: [PATCH 05/38] feat: create google drive ingest component (#3129) * feat: create google drive ingest component It adds the basic implementation of the Google Drive Loader. * feat: create google drive ingest component Created a basic Google Drive loader component * Updated the Icon * updated the formatting ran make format to update the formatting * Addressed comments --- .../langflow/components/data/GoogleDrive.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/backend/base/langflow/components/data/GoogleDrive.py diff --git a/src/backend/base/langflow/components/data/GoogleDrive.py b/src/backend/base/langflow/components/data/GoogleDrive.py new file mode 100644 index 000000000000..4cc7450ebd20 --- /dev/null +++ b/src/backend/base/langflow/components/data/GoogleDrive.py @@ -0,0 +1,87 @@ +import json +from typing import Optional +from google.oauth2.credentials import Credentials +from google.auth.exceptions import RefreshError +from langflow.custom import Component +from langflow.inputs import MessageTextInput +from langflow.io import SecretStrInput +from langflow.template import Output +from langflow.schema import Data +from langchain_google_community import GoogleDriveLoader +from langflow.helpers.data import docs_to_data + +from json.decoder import JSONDecodeError + + +class GoogleDriveComponent(Component): + display_name = "Google Drive Loader" + description = "Loads documents from Google Drive using provided credentials." + icon = "Google" + + inputs = [ + SecretStrInput( + name="json_string", + display_name="JSON String of the Service Account Token", + info="JSON string containing OAuth 2.0 access token information for service account access", + required=True, + ), + MessageTextInput( + name="document_id", display_name="Document ID", info="Single Google Drive document ID", required=True + ), + ] + + outputs = [ + Output(display_name="Loaded Documents", name="docs", method="load_documents"), + ] + + def load_documents(self) -> Data: + class CustomGoogleDriveLoader(GoogleDriveLoader): + creds: Optional[Credentials] = None + """Credentials object to be passed directly.""" + + def _load_credentials(self): + """Load credentials from the provided creds attribute or fallback to the original method.""" + if self.creds: + return self.creds + else: + raise ValueError("No credentials provided.") + + class Config: + arbitrary_types_allowed = True + + json_string = self.json_string + + document_ids = [self.document_id] + if len(document_ids) != 1: + raise ValueError("Expected a single document ID") + + # TODO: Add validation to check if the document ID is valid + + # Load the token information from the JSON string + try: + token_info = json.loads(json_string) + except JSONDecodeError as e: + raise ValueError("Invalid JSON string") from e + + # Initialize the custom loader with the provided credentials and document IDs + loader = CustomGoogleDriveLoader( + creds=Credentials.from_authorized_user_info(token_info), document_ids=document_ids + ) + + # Load the documents + try: + docs = loader.load() + # catch google.auth.exceptions.RefreshError + except RefreshError as e: + raise ValueError( + "Authentication error: Unable to refresh authentication token. Please try to reauthenticate." + ) from e + except Exception as e: + raise ValueError(f"Error loading documents: {e}") from e + + assert len(docs) == 1, "Expected a single document to be loaded." + + data = docs_to_data(docs) + # Return the loaded documents + self.status = data + return Data(data={"text": data}) From e42b6bdb94ca84a7c91bb9b9cbea432ccf034eb0 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Aug 2024 17:21:21 -0300 Subject: [PATCH 06/38] fix: change ValueError into Warning to allow disconnected flows to run and other small fixes (#3249) * fix: add task to end all traces on asyncio.CancelledError in build_flow function for better cleanup handling * fix: replace ValueError with warnings in Graph class when vertices exist without edges for better logging and handling * chore: add type annotations to test_vector_store_rag_add function * feat: Fix assertion in test_create_flows to check for substring in name field The assertion in the test_create_flows function was modified to check if the name field contains the substring "Flow 1" instead of an exact match. This change allows for more flexibility in the test and ensures that the test passes even if there are additional characters in the name field. --- src/backend/base/langflow/api/v1/chat.py | 1 + src/backend/base/langflow/graph/graph/base.py | 3 ++- src/backend/tests/unit/graph/graph/test_base.py | 2 +- .../initial_setup/starter_projects/test_vector_store_rag.py | 2 +- src/backend/tests/unit/test_database.py | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index a58b667fb02f..143937d1d146 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -370,6 +370,7 @@ async def event_generator(queue: asyncio.Queue, client_consumed_queue: asyncio.Q try: await asyncio.gather(*tasks) except asyncio.CancelledError: + background_tasks.add_task(graph.end_all_traces) for task in tasks: task.cancel() return diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 42de21ac918e..d1dbe1c71bd4 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -7,6 +7,7 @@ from functools import partial from itertools import chain from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Tuple, Type, Union +import warnings import nest_asyncio from loguru import logger @@ -1425,7 +1426,7 @@ def _build_edges(self) -> List[ContractEdge]: new_edge = self.build_edge(edge) edges.add(new_edge) if self.vertices and not edges: - raise ValueError("Graph has vertices but no edges") + warnings.warn("Graph has vertices but no edges") return list(edges) def build_edge(self, edge: EdgeData) -> ContractEdge: diff --git a/src/backend/tests/unit/graph/graph/test_base.py b/src/backend/tests/unit/graph/graph/test_base.py index 45be6c609ac7..59908d9dca43 100644 --- a/src/backend/tests/unit/graph/graph/test_base.py +++ b/src/backend/tests/unit/graph/graph/test_base.py @@ -32,7 +32,7 @@ async def test_graph(): graph = Graph() graph.add_component("chat_input", chat_input) graph.add_component("chat_output", chat_output) - with pytest.raises(ValueError, match="Graph has vertices but no edges"): + with pytest.warns(UserWarning, match="Graph has vertices but no edges"): graph.prepare() diff --git a/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py b/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py index 587c65779a2d..e216c0c567cc 100644 --- a/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py +++ b/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py @@ -212,7 +212,7 @@ def test_vector_store_rag_dump_components_and_edges(ingestion_graph, rag_graph): assert (source, target) in expected_rag_edges, f"Edge {source} -> {target} not found" -def test_vector_store_rag_add(ingestion_graph, rag_graph): +def test_vector_store_rag_add(ingestion_graph: Graph, rag_graph: Graph): ingestion_graph_copy = copy.deepcopy(ingestion_graph) rag_graph_copy = copy.deepcopy(rag_graph) ingestion_graph_copy += rag_graph_copy diff --git a/src/backend/tests/unit/test_database.py b/src/backend/tests/unit/test_database.py index aebdef327129..44e347f1e118 100644 --- a/src/backend/tests/unit/test_database.py +++ b/src/backend/tests/unit/test_database.py @@ -212,7 +212,7 @@ def test_create_flows(client: TestClient, session: Session, json_flow: str, logg # Check response data response_data = response.json() assert len(response_data) == 2 - assert response_data[0]["name"] == "Flow 1" + assert "Flow 1" in response_data[0]["name"] assert response_data[0]["description"] == "description" assert response_data[0]["data"] == data assert response_data[1]["name"] == "Flow 2" @@ -241,7 +241,7 @@ def test_upload_file(client: TestClient, session: Session, json_flow: str, logge # Check response data response_data = response.json() assert len(response_data) == 2 - assert response_data[0]["name"] == "Flow 1" + assert "Flow 1" in response_data[0]["name"] assert response_data[0]["description"] == "description" assert response_data[0]["data"] == data assert response_data[1]["name"] == "Flow 2" From d606a4dac3b1b250b2e4333c758f8f7edbeaf892 Mon Sep 17 00:00:00 2001 From: vinicius Mello <45274355+vmellos@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:41:42 -0400 Subject: [PATCH 07/38] feat: add huggingface endpoint retry (#3236) * feat: added retry when calling huggingface endpoint * chore: added default value to retry input * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../components/models/HuggingFaceModel.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/backend/base/langflow/components/models/HuggingFaceModel.py b/src/backend/base/langflow/components/models/HuggingFaceModel.py index 313d440019df..069d63d18301 100644 --- a/src/backend/base/langflow/components/models/HuggingFaceModel.py +++ b/src/backend/base/langflow/components/models/HuggingFaceModel.py @@ -1,9 +1,11 @@ +from tenacity import retry, stop_after_attempt, wait_fixed + from langchain_community.chat_models.huggingface import ChatHuggingFace from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint from langflow.base.models.model import LCModelComponent from langflow.field_typing import LanguageModel -from langflow.io import DictInput, DropdownInput, SecretStrInput, StrInput +from langflow.io import DictInput, DropdownInput, SecretStrInput, StrInput, IntInput class HuggingFaceEndpointsComponent(LCModelComponent): @@ -26,8 +28,24 @@ class HuggingFaceEndpointsComponent(LCModelComponent): ), SecretStrInput(name="huggingfacehub_api_token", display_name="API token", password=True), DictInput(name="model_kwargs", display_name="Model Keyword Arguments", advanced=True), + IntInput(name="retry_attempts", display_name="Retry Attempts", value=1), ] + def create_huggingface_endpoint(self, endpoint_url, task, huggingfacehub_api_token, model_kwargs): + @retry(stop=stop_after_attempt(self.retry_attempts), wait=wait_fixed(2)) + def _attempt_create(): + try: + return HuggingFaceEndpoint( # type: ignore + endpoint_url=endpoint_url, + task=task, + huggingfacehub_api_token=huggingfacehub_api_token, + model_kwargs=model_kwargs, + ) + except Exception as e: + raise ValueError("Could not connect to HuggingFace Endpoints API.") from e + + return _attempt_create() + def build_model(self) -> LanguageModel: # type: ignore[type-var] endpoint_url = self.endpoint_url task = self.task @@ -35,12 +53,7 @@ def build_model(self) -> LanguageModel: # type: ignore[type-var] model_kwargs = self.model_kwargs or {} try: - llm = HuggingFaceEndpoint( # type: ignore - endpoint_url=endpoint_url, - task=task, - huggingfacehub_api_token=huggingfacehub_api_token, - model_kwargs=model_kwargs, - ) + llm = self.create_huggingface_endpoint(endpoint_url, task, huggingfacehub_api_token, model_kwargs) except Exception as e: raise ValueError("Could not connect to HuggingFace Endpoints API.") from e From edb3c9e4d2a2acda5cd82ce719ec984b4f0a601b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Aug 2024 19:23:02 -0300 Subject: [PATCH 08/38] fix: change sync_get_file_content_dicts to use get_file_content_dicts (#3250) feat: refactor sync_get_file_content_dicts to use get_file_content_dicts Refactor the `sync_get_file_content_dicts` method in the `Message` class to use the `get_file_content_dicts` method instead. This change improves the code by using a more descriptive and accurate method name. --- src/backend/base/langflow/schema/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index bd1c3d848f39..53d7f014138b 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -164,7 +164,7 @@ def serialize_text(self, value): return value def sync_get_file_content_dicts(self): - coro = self.aget_file_content_dicts() + coro = self.get_file_content_dicts() loop = asyncio.get_event_loop() return loop.run_until_complete(coro) From 56ecb18ef5feea651e85894c9803c090441f9131 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 09:42:21 -0300 Subject: [PATCH 09/38] feat: Improve caching logic and add disk caching option (#3246) * feat: add diskcache package version 5.6.3 to poetry.lock and pyproject.toml for improved caching functionality * refactor: simplify CacheMiss import in cache service files for better clarity and maintainability * feat: Add AsyncDiskCache class for disk-based caching * feat: Add disk caching option in CacheServiceFactory with AsyncDiskCache * feat: Restrict cache_type to specific literals: async, redis, memory, disk for enhanced type safety and clarity * feat: Change get_requester_result to async await for proper async handling in Vertex class * fix: Update outputs dictionary in ResultData class to use key-value pairs for better readability and maintainability * fix: Improve caching logic in Graph class by ensuring vertex builds properly handle exceptions and cache updates more reliably * feat: Add teardown method to AsyncDiskCache for clearing cache directory during cleanup process * fix: Correct variable name in Graph class to ensure proper handling of vertex results in caching logic * feat: Clear AsyncDiskCache on initialization to align behavior with in-memory cache until frontend handling is implemented --- src/backend/base/langflow/graph/graph/base.py | 58 ++++++----- src/backend/base/langflow/graph/schema.py | 2 +- .../base/langflow/graph/vertex/base.py | 2 +- .../base/langflow/services/cache/disk.py | 96 +++++++++++++++++++ .../base/langflow/services/cache/factory.py | 6 ++ .../base/langflow/services/cache/service.py | 4 +- .../base/langflow/services/cache/utils.py | 3 + .../base/langflow/services/settings/base.py | 4 +- src/backend/base/poetry.lock | 13 ++- src/backend/base/pyproject.toml | 1 + 10 files changed, 157 insertions(+), 32 deletions(-) create mode 100644 src/backend/base/langflow/services/cache/disk.py diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index d1dbe1c71bd4..918ce23de023 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -1126,41 +1126,51 @@ async def build_vertex( self.run_manager.add_to_vertices_being_run(vertex_id) try: params = "" - if vertex.frozen: + should_build = False + if not vertex.frozen: + should_build = True + else: # Check the cache for the vertex if get_cache is not None: cached_result = await get_cache(key=vertex.id) else: cached_result = None if isinstance(cached_result, CacheMiss): - await vertex.build( - user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files - ) - if set_cache is not None: - await set_cache(key=vertex.id, data=vertex) - if cached_result and not isinstance(cached_result, CacheMiss): - cached_vertex = cached_result["result"] - # Now set update the vertex with the cached vertex - vertex._built = cached_vertex._built - vertex.result = cached_vertex.result - vertex.results = cached_vertex.results - vertex.artifacts = cached_vertex.artifacts - vertex._built_object = cached_vertex._built_object - vertex._custom_component = cached_vertex._custom_component - if vertex.result is not None: - vertex.result.used_frozen_result = True + should_build = True else: - await vertex.build( - user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files - ) - if set_cache is not None: - await set_cache(key=vertex.id, data=vertex) - else: + try: + cached_vertex_dict = cached_result["result"] + # Now set update the vertex with the cached vertex + vertex._built = cached_vertex_dict["_built"] + vertex.artifacts = cached_vertex_dict["artifacts"] + vertex._built_object = cached_vertex_dict["_built_object"] + vertex._built_result = cached_vertex_dict["_built_result"] + vertex._data = cached_vertex_dict["_data"] + vertex.results = cached_vertex_dict["results"] + try: + vertex._finalize_build() + if vertex.result is not None: + vertex.result.used_frozen_result = True + except Exception: + should_build = True + except KeyError: + should_build = True + + if should_build: await vertex.build( user_id=user_id, inputs=inputs_dict, fallback_to_env_vars=fallback_to_env_vars, files=files ) if set_cache is not None: - await set_cache(key=vertex.id, data=vertex) + vertex_dict = { + "_built": vertex._built, + "results": vertex.results, + "artifacts": vertex.artifacts, + "_built_object": vertex._built_object, + "_built_result": vertex._built_result, + "_data": vertex._data, + } + + await set_cache(key=vertex.id, data=vertex_dict) if vertex.result is not None: params = f"{vertex._built_object_repr()}{params}" diff --git a/src/backend/base/langflow/graph/schema.py b/src/backend/base/langflow/graph/schema.py index eab0040c6d3b..fdabcdaaa64a 100644 --- a/src/backend/base/langflow/graph/schema.py +++ b/src/backend/base/langflow/graph/schema.py @@ -43,7 +43,7 @@ def validate_model(cls, values): stream_url = StreamURL(location=message["stream_url"]) values["outputs"].update({key: OutputValue(message=stream_url, type=message["type"])}) elif "type" in message: - values["outputs"].update({OutputValue(message=message, type=message["type"])}) + values["outputs"].update({key: OutputValue(message=message, type=message["type"])}) return values diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 8284736acf3d..0b7941734b48 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -753,7 +753,7 @@ async def build( return if self.frozen and self._built: - return self.get_requester_result(requester) + return await self.get_requester_result(requester) elif self._built and requester is not None: # This means that the vertex has already been built # and we are just getting the result for the requester diff --git a/src/backend/base/langflow/services/cache/disk.py b/src/backend/base/langflow/services/cache/disk.py new file mode 100644 index 000000000000..dbbd85f1335c --- /dev/null +++ b/src/backend/base/langflow/services/cache/disk.py @@ -0,0 +1,96 @@ +import asyncio +import pickle +import time +from typing import Generic, Optional + +from diskcache import Cache +from loguru import logger + +from langflow.services.cache.base import AsyncBaseCacheService, AsyncLockType +from langflow.services.cache.utils import CACHE_MISS + + +class AsyncDiskCache(AsyncBaseCacheService, Generic[AsyncLockType]): # type: ignore + def __init__(self, cache_dir, max_size=None, expiration_time=3600): + self.cache = Cache(cache_dir) + # Let's clear the cache for now to maintain a similar + # behavior as the in-memory cache + # Later we should implement endpoints for the frontend to grab + # output logs from the cache + if len(self.cache) > 0: + self.cache.clear() + self.lock = asyncio.Lock() + self.max_size = max_size + self.expiration_time = expiration_time + + async def get(self, key, lock: Optional[asyncio.Lock] = None): + if not lock: + async with self.lock: + return await self._get(key) + else: + return await self._get(key) + + async def _get(self, key): + item = await asyncio.to_thread(self.cache.get, key, default=None) + if item: + if time.time() - item["time"] < self.expiration_time: + await asyncio.to_thread(self.cache.touch, key) # Refresh the expiry time + return pickle.loads(item["value"]) if isinstance(item["value"], bytes) else item["value"] + else: + logger.info(f"Cache item for key '{key}' has expired and will be deleted.") + await self._delete(key) # Log before deleting the expired item + return CACHE_MISS + + async def set(self, key, value, lock: Optional[asyncio.Lock] = None): + if not lock: + async with self.lock: + await self._set(key, value) + else: + await self._set(key, value) + + async def _set(self, key, value): + if self.max_size and len(self.cache) >= self.max_size: + await asyncio.to_thread(self.cache.cull) + item = {"value": pickle.dumps(value) if not isinstance(value, (str, bytes)) else value, "time": time.time()} + await asyncio.to_thread(self.cache.set, key, item) + + async def delete(self, key, lock: Optional[asyncio.Lock] = None): + if not lock: + async with self.lock: + await self._delete(key) + else: + await self._delete(key) + + async def _delete(self, key): + await asyncio.to_thread(self.cache.delete, key) + + async def clear(self, lock: Optional[asyncio.Lock] = None): + if not lock: + async with self.lock: + await self._clear() + else: + await self._clear() + + async def _clear(self): + await asyncio.to_thread(self.cache.clear) + + async def upsert(self, key, value, lock: Optional[asyncio.Lock] = None): + if not lock: + async with self.lock: + await self._upsert(key, value) + else: + await self._upsert(key, value) + + async def _upsert(self, key, value): + existing_value = await self.get(key) + if existing_value is not CACHE_MISS and isinstance(existing_value, dict) and isinstance(value, dict): + existing_value.update(value) + value = existing_value + await self.set(key, value) + + def __contains__(self, key): + return asyncio.run(asyncio.to_thread(self.cache.__contains__, key)) + + async def teardown(self): + # Clean up the cache directory + self.cache.clear(retry=True) diff --git a/src/backend/base/langflow/services/cache/factory.py b/src/backend/base/langflow/services/cache/factory.py index 5cc6b12afe0e..74364dbfc0e5 100644 --- a/src/backend/base/langflow/services/cache/factory.py +++ b/src/backend/base/langflow/services/cache/factory.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING +from langflow.services.cache.disk import AsyncDiskCache from langflow.services.cache.service import AsyncInMemoryCache, CacheService, RedisCache, ThreadingInMemoryCache from langflow.services.factory import ServiceFactory from langflow.utils.logger import logger @@ -36,3 +37,8 @@ def create(self, settings_service: "SettingsService"): return ThreadingInMemoryCache(expiration_time=settings_service.settings.cache_expire) elif settings_service.settings.cache_type == "async": return AsyncInMemoryCache(expiration_time=settings_service.settings.cache_expire) + elif settings_service.settings.cache_type == "disk": + return AsyncDiskCache( + cache_dir=settings_service.settings.config_dir, + expiration_time=settings_service.settings.cache_expire, + ) diff --git a/src/backend/base/langflow/services/cache/service.py b/src/backend/base/langflow/services/cache/service.py index 3d4131c239c1..021c33f90281 100644 --- a/src/backend/base/langflow/services/cache/service.py +++ b/src/backend/base/langflow/services/cache/service.py @@ -8,9 +8,7 @@ from loguru import logger from langflow.services.cache.base import AsyncBaseCacheService, AsyncLockType, CacheService, LockType -from langflow.services.cache.utils import CacheMiss - -CACHE_MISS = CacheMiss() +from langflow.services.cache.utils import CACHE_MISS class ThreadingInMemoryCache(CacheService, Generic[LockType]): # type: ignore diff --git a/src/backend/base/langflow/services/cache/utils.py b/src/backend/base/langflow/services/cache/utils.py index a89963f5681c..c2f3c961124c 100644 --- a/src/backend/base/langflow/services/cache/utils.py +++ b/src/backend/base/langflow/services/cache/utils.py @@ -166,3 +166,6 @@ def update_build_status(cache_service, flow_id: str, status: "BuildStatus"): cache_service[flow_id] = cached_flow cached_flow["status"] = status cache_service[flow_id] = cached_flow + + +CACHE_MISS = CacheMiss() diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py index 658edd57e1c7..88c2a64b49fe 100644 --- a/src/backend/base/langflow/services/settings/base.py +++ b/src/backend/base/langflow/services/settings/base.py @@ -3,7 +3,7 @@ import os from pathlib import Path from shutil import copy2 -from typing import Any, List, Optional, Tuple, Type +from typing import Any, List, Literal, Optional, Tuple, Type import orjson import yaml @@ -79,7 +79,7 @@ class Settings(BaseSettings): """SQLite pragmas to use when connecting to the database.""" # cache configuration - cache_type: str = "async" + cache_type: Literal["async", "redis", "memory", "disk"] = "async" """The cache type can be 'async' or 'redis'.""" cache_expire: int = 3600 """The cache expire in seconds.""" diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index e52b0c5b7754..8deb46e0bfab 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -1208,6 +1208,17 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] +[[package]] +name = "diskcache" +version = "5.6.3" +description = "Disk Cache -- Disk and file backed persistent cache." +optional = false +python-versions = ">=3" +files = [ + {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, + {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, +] + [[package]] name = "distlib" version = "0.3.8" @@ -7654,4 +7665,4 @@ local = [] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "747dad35b9e5b1338a989ea6bfd4ac3465ba34f792639aeabda3c1ca9b40c689" +content-hash = "fe6710d7325bc2cceeaa298d94d6f1157cfe1533c2acbabe3ecdca5594d9e007" diff --git a/src/backend/base/pyproject.toml b/src/backend/base/pyproject.toml index d98c673e0767..a8de78d9e01f 100644 --- a/src/backend/base/pyproject.toml +++ b/src/backend/base/pyproject.toml @@ -79,6 +79,7 @@ filelock = "^3.15.4" grandalf = "^0.8.0" crewai = "^0.36.0" spider-client = "^0.0.27" +diskcache = "^5.6.3" [tool.poetry.extras] From 645c723d21aec1011bfe8d48c3a76537940ae385 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 10:59:49 -0300 Subject: [PATCH 10/38] feat: start using dev mode flag and add exception filter in logger (#3260) * refactor: reorganize logger module and add setup.py for logging configuration * refactor: update logger import path to align with recent module restructuring * refactor: add logging configuration parameter to Graph initialization for improved logging setup flexibility * feat: create logging init module for improved logger configuration and management * refactor: update Settings class to include development mode flag and associated validator for enhanced configuration management * refactor: enhance logger.py with DEV mode handling and TypedDict for log configuration settings * feat: add settings module with DEV mode flag and helper functions for setting development state * refactor: update flow assertions in tests to check for Data object type instead of Flow object type * feat: add deepcopy method to Graph class to copy start and end components, ensuring proper graph cloning functionality * feat: implement deepcopy method in Component class for proper cloning of inputs and configuration attributes * feat: enhance attribute access in Component class to support backwards-compatible private attributes retrieval * feat: improve test assertion for list_flows in custom component to display types of returned objects for better debugging * feat: refactor imports in constants.py and remove redundant Data class definition for cleaner code structure * feat: refactor imports in logger.py to include NotRequired from typing_extensions for better type hinting support --- src/backend/base/langflow/__main__.py | 2 +- src/backend/base/langflow/api/log_router.py | 2 +- .../custom/custom_component/component.py | 24 ++++++++++ .../base/langflow/field_typing/constants.py | 7 +-- src/backend/base/langflow/graph/graph/base.py | 44 ++++++++++++++++++- src/backend/base/langflow/load/load.py | 2 +- src/backend/base/langflow/logging/__init__.py | 4 ++ .../langflow/{utils => logging}/logger.py | 16 ++++++- src/backend/base/langflow/logging/setup.py | 16 +++++++ src/backend/base/langflow/main.py | 2 +- src/backend/base/langflow/server.py | 2 +- .../base/langflow/services/cache/factory.py | 2 +- .../base/langflow/services/settings/base.py | 9 ++++ src/backend/base/langflow/settings.py | 10 +++++ src/backend/base/langflow/utils/util.py | 2 +- .../unit/test_custom_component_with_client.py | 6 ++- src/backend/tests/unit/test_logger.py | 2 +- 17 files changed, 133 insertions(+), 19 deletions(-) create mode 100644 src/backend/base/langflow/logging/__init__.py rename src/backend/base/langflow/{utils => logging}/logger.py (95%) create mode 100644 src/backend/base/langflow/logging/setup.py create mode 100644 src/backend/base/langflow/settings.py diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py index 25e1ca4bfd23..c342bf218922 100644 --- a/src/backend/base/langflow/__main__.py +++ b/src/backend/base/langflow/__main__.py @@ -26,7 +26,7 @@ from langflow.services.deps import get_db_service, get_settings_service, session_scope from langflow.services.settings.constants import DEFAULT_SUPERUSER from langflow.services.utils import initialize_services -from langflow.utils.logger import configure, logger +from langflow.logging.logger import configure, logger from langflow.utils.util import update_settings console = Console() diff --git a/src/backend/base/langflow/api/log_router.py b/src/backend/base/langflow/api/log_router.py index 45f7b3e510c6..4e4aa9a7dbad 100644 --- a/src/backend/base/langflow/api/log_router.py +++ b/src/backend/base/langflow/api/log_router.py @@ -5,7 +5,7 @@ from fastapi import APIRouter, Query, HTTPException, Request from fastapi.responses import JSONResponse, StreamingResponse from http import HTTPStatus -from langflow.utils.logger import log_buffer +from langflow.logging.logger import log_buffer log_router = APIRouter(tags=["Log"]) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 5a222e8a9610..997736bf8f53 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -1,4 +1,5 @@ import inspect +from copy import deepcopy from typing import TYPE_CHECKING, Any, Callable, ClassVar, List, Optional, Union, get_type_hints from uuid import UUID @@ -34,6 +35,7 @@ class Component(CustomComponent): def __init__(self, **kwargs): # if key starts with _ it is a config # else it is an input + inputs = {} config = {} for key, value in kwargs.items(): @@ -53,6 +55,8 @@ def __init__(self, **kwargs): config = config or {} if "_id" not in config: config |= {"_id": f"{self.__class__.__name__}-{nanoid.generate(size=5)}"} + self.__inputs = inputs + self.__config = config super().__init__(**config) if hasattr(self, "_trace_type"): self.trace_type = self._trace_type @@ -66,6 +70,24 @@ def __init__(self, **kwargs): self._set_output_types() self.set_class_code() + def __deepcopy__(self, memo): + if id(self) in memo: + return memo[id(self)] + kwargs = deepcopy(self.__config) + kwargs["inputs"] = deepcopy(self.__inputs) + new_component = type(self)(**kwargs) + new_component._code = self._code + new_component._outputs = self._outputs + new_component._inputs = self._inputs + new_component._edges = self._edges + new_component._components = self._components + new_component._parameters = self._parameters + new_component._attributes = self._attributes + new_component._output_logs = self._output_logs + new_component._logs = self._logs + memo[id(self)] = new_component + return new_component + def set_class_code(self): # Get the source code of the calling class if self._code: @@ -331,6 +353,8 @@ def __getattr__(self, name: str) -> Any: return self.__dict__["_inputs"][name].value if name in BACKWARDS_COMPATIBLE_ATTRIBUTES: return self.__dict__[f"_{name}"] + if name.startswith("_") and name[1:] in BACKWARDS_COMPATIBLE_ATTRIBUTES: + return self.__dict__[name] raise AttributeError(f"{name} not found in {self.__class__.__name__}") def _set_input_value(self, name: str, value: Any): diff --git a/src/backend/base/langflow/field_typing/constants.py b/src/backend/base/langflow/field_typing/constants.py index a5857ee12d41..dfa8309e71cd 100644 --- a/src/backend/base/langflow/field_typing/constants.py +++ b/src/backend/base/langflow/field_typing/constants.py @@ -13,10 +13,11 @@ from langchain_core.output_parsers import BaseOutputParser from langchain_core.prompts import BasePromptTemplate, ChatPromptTemplate, PromptTemplate from langchain_core.retrievers import BaseRetriever -from langchain_core.tools import Tool, BaseTool +from langchain_core.tools import BaseTool, Tool from langchain_core.vectorstores import VectorStore, VectorStoreRetriever from langchain_text_splitters import TextSplitter +from langflow.schema.data import Data from langflow.schema.message import Message NestedDict: TypeAlias = Dict[str, Union[str, Dict]] @@ -33,10 +34,6 @@ class Object: pass -class Data: - pass - - class Code: pass diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 918ce23de023..a47b11b998f5 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -2,12 +2,12 @@ import copy import json import uuid +import warnings from collections import defaultdict, deque from datetime import datetime, timezone from functools import partial from itertools import chain from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Tuple, Type, Union -import warnings import nest_asyncio from loguru import logger @@ -24,6 +24,7 @@ from langflow.graph.vertex.base import Vertex, VertexStates from langflow.graph.vertex.schema import NodeData from langflow.graph.vertex.types import ComponentVertex, InterfaceVertex, StateVertex +from langflow.logging.logger import LogConfig, configure from langflow.schema import Data from langflow.schema.schema import INPUT_FIELD_NAME, InputType from langflow.services.cache.utils import CacheMiss @@ -47,6 +48,7 @@ def __init__( flow_id: Optional[str] = None, flow_name: Optional[str] = None, user_id: Optional[str] = None, + log_config: Optional[LogConfig] = None, ) -> None: """ Initializes a new instance of the Graph class. @@ -56,6 +58,11 @@ def __init__( edges (List[Dict[str, str]]): A list of dictionaries representing the edges of the graph. flow_id (Optional[str], optional): The ID of the flow. Defaults to None. """ + if not log_config: + log_config = {"disable": False} + configure(**log_config) + self._start = start + self._end = end self._prepared = False self._runs = 0 self._updates = 0 @@ -803,7 +810,6 @@ def __getstate__(self): "vertices_layers": self.vertices_layers, "vertices_to_run": self.vertices_to_run, "stop_vertex": self.stop_vertex, - "vertex_map": self.vertex_map, "_run_queue": self._run_queue, "_first_layer": self._first_layer, "_vertices": self._vertices, @@ -814,6 +820,39 @@ def __getstate__(self): "_sorted_vertices_layers": self._sorted_vertices_layers, } + def __deepcopy__(self, memo): + # Check if we've already copied this instance + if id(self) in memo: + return memo[id(self)] + + if self._start is not None and self._end is not None: + # Deep copy start and end components + start_copy = copy.deepcopy(self._start, memo) + end_copy = copy.deepcopy(self._end, memo) + new_graph = type(self)( + start_copy, + end_copy, + copy.deepcopy(self.flow_id, memo), + copy.deepcopy(self.flow_name, memo), + copy.deepcopy(self.user_id, memo), + ) + else: + # Create a new graph without start and end, but copy flow_id, flow_name, and user_id + new_graph = type(self)( + None, + None, + copy.deepcopy(self.flow_id, memo), + copy.deepcopy(self.flow_name, memo), + copy.deepcopy(self.user_id, memo), + ) + # Deep copy vertices and edges + new_graph.add_nodes_and_edges(copy.deepcopy(self._vertices, memo), copy.deepcopy(self._edges, memo)) + + # Store the newly created object in memo + memo[id(self)] = new_graph + + return new_graph + def __setstate__(self, state): run_manager = state["run_manager"] if isinstance(run_manager, RunnableVerticesManager): @@ -821,6 +860,7 @@ def __setstate__(self, state): else: state["run_manager"] = RunnableVerticesManager.from_dict(run_manager) self.__dict__.update(state) + self.vertex_map = {vertex.id: vertex for vertex in self.vertices} self.state_manager = GraphStateManager() self.tracing_service = get_tracing_service() self.set_run_id(self._run_id) diff --git a/src/backend/base/langflow/load/load.py b/src/backend/base/langflow/load/load.py index b56f22c81b8e..972c2c7e7d5f 100644 --- a/src/backend/base/langflow/load/load.py +++ b/src/backend/base/langflow/load/load.py @@ -8,7 +8,7 @@ from langflow.graph import Graph from langflow.graph.schema import RunOutputs from langflow.processing.process import process_tweaks, run_graph -from langflow.utils.logger import configure +from langflow.logging.logger import configure from langflow.utils.util import update_settings diff --git a/src/backend/base/langflow/logging/__init__.py b/src/backend/base/langflow/logging/__init__.py new file mode 100644 index 000000000000..b7d7bb5a1cf5 --- /dev/null +++ b/src/backend/base/langflow/logging/__init__.py @@ -0,0 +1,4 @@ +from .logger import configure, logger +from .setup import disable_logging, enable_logging + +__all__ = ["configure", "logger", "disable_logging", "enable_logging"] diff --git a/src/backend/base/langflow/utils/logger.py b/src/backend/base/langflow/logging/logger.py similarity index 95% rename from src/backend/base/langflow/utils/logger.py rename to src/backend/base/langflow/logging/logger.py index 55c33ce63c5c..01b146b68afe 100644 --- a/src/backend/base/langflow/utils/logger.py +++ b/src/backend/base/langflow/logging/logger.py @@ -2,15 +2,18 @@ import logging import os import sys -from pathlib import Path from collections import deque +from pathlib import Path from threading import Lock, Semaphore -from typing import Optional +from typing import Optional, TypedDict import orjson from loguru import logger from platformdirs import user_cache_dir from rich.logging import RichHandler +from typing_extensions import NotRequired + +from langflow.settings import DEV VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] @@ -129,6 +132,15 @@ def serialize_log(record): def patching(record): record["extra"]["serialized"] = serialize_log(record) + if DEV is False: + record.pop("exception", None) + + +class LogConfig(TypedDict): + log_level: NotRequired[str] + log_file: NotRequired[Path] + disable: NotRequired[bool] + log_env: NotRequired[str] def configure( diff --git a/src/backend/base/langflow/logging/setup.py b/src/backend/base/langflow/logging/setup.py new file mode 100644 index 000000000000..fdf1e22b6945 --- /dev/null +++ b/src/backend/base/langflow/logging/setup.py @@ -0,0 +1,16 @@ +from loguru import logger + +LOGGING_CONFIGURED = False + + +def disable_logging(): + global LOGGING_CONFIGURED + if not LOGGING_CONFIGURED: + logger.disable("langflow") + LOGGING_CONFIGURED = True + + +def enable_logging(): + global LOGGING_CONFIGURED + logger.enable("langflow") + LOGGING_CONFIGURED = True diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index e4a2611f8eec..b79f092bcf76 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -31,7 +31,7 @@ from langflow.services.deps import get_cache_service, get_settings_service, get_telemetry_service from langflow.services.plugins.langfuse_plugin import LangfuseInstance from langflow.services.utils import initialize_services, teardown_services -from langflow.utils.logger import configure +from langflow.logging.logger import configure # Ignore Pydantic deprecation warnings from Langchain warnings.filterwarnings("ignore", category=PydanticDeprecatedSince20) diff --git a/src/backend/base/langflow/server.py b/src/backend/base/langflow/server.py index 67061fbdd7da..0c3a21a2e25f 100644 --- a/src/backend/base/langflow/server.py +++ b/src/backend/base/langflow/server.py @@ -6,7 +6,7 @@ from gunicorn.app.base import BaseApplication # type: ignore from uvicorn.workers import UvicornWorker -from langflow.utils.logger import InterceptHandler # type: ignore +from langflow.logging.logger import InterceptHandler # type: ignore class LangflowUvicornWorker(UvicornWorker): diff --git a/src/backend/base/langflow/services/cache/factory.py b/src/backend/base/langflow/services/cache/factory.py index 74364dbfc0e5..32bb94f872ff 100644 --- a/src/backend/base/langflow/services/cache/factory.py +++ b/src/backend/base/langflow/services/cache/factory.py @@ -3,7 +3,7 @@ from langflow.services.cache.disk import AsyncDiskCache from langflow.services.cache.service import AsyncInMemoryCache, CacheService, RedisCache, ThreadingInMemoryCache from langflow.services.factory import ServiceFactory -from langflow.utils.logger import logger +from langflow.logging.logger import logger if TYPE_CHECKING: from langflow.services.settings.service import SettingsService diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py index 88c2a64b49fe..592279af0766 100644 --- a/src/backend/base/langflow/services/settings/base.py +++ b/src/backend/base/langflow/services/settings/base.py @@ -66,6 +66,7 @@ class Settings(BaseSettings): """Define if langflow database should be saved in LANGFLOW_CONFIG_DIR or in the langflow directory (i.e. in the package directory).""" dev: bool = False + """If True, Langflow will run in development mode.""" database_url: Optional[str] = None """Database URL for Langflow. If not provided, Langflow will use a SQLite database.""" pool_size: int = 10 @@ -151,6 +152,14 @@ class Settings(BaseSettings): vertex_builds_storage_enabled: bool = True """If set to True, Langflow will keep track of each vertex builds (outputs) in the UI for any flow.""" + @field_validator("dev") + @classmethod + def set_dev(cls, value): + from langflow.settings import set_dev + + set_dev(value) + return value + @field_validator("user_agent", mode="after") @classmethod def set_user_agent(cls, value): diff --git a/src/backend/base/langflow/settings.py b/src/backend/base/langflow/settings.py new file mode 100644 index 000000000000..9a1d985c359b --- /dev/null +++ b/src/backend/base/langflow/settings.py @@ -0,0 +1,10 @@ +DEV = False + + +def _set_dev(value): + global DEV + DEV = value + + +def set_dev(value): + _set_dev(value) diff --git a/src/backend/base/langflow/utils/util.py b/src/backend/base/langflow/utils/util.py index 1c91869041f3..cb3eec752d03 100644 --- a/src/backend/base/langflow/utils/util.py +++ b/src/backend/base/langflow/utils/util.py @@ -12,7 +12,7 @@ from langflow.services.deps import get_settings_service from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS from langflow.utils import constants -from langflow.utils.logger import logger +from langflow.logging.logger import logger def unescape_string(s: str): diff --git a/src/backend/tests/unit/test_custom_component_with_client.py b/src/backend/tests/unit/test_custom_component_with_client.py index 3be2ce657f20..ab8e7859e80f 100644 --- a/src/backend/tests/unit/test_custom_component_with_client.py +++ b/src/backend/tests/unit/test_custom_component_with_client.py @@ -1,7 +1,7 @@ import pytest from langflow.custom.custom_component.custom_component import CustomComponent -from langflow.services.database.models.flow import Flow +from langflow.field_typing.constants import Data @pytest.fixture @@ -20,4 +20,6 @@ def component(client, active_user): def test_list_flows_flow_objects(component): flows = component.list_flows() - assert all(isinstance(flow, Flow) for flow in flows) + are_flows = [isinstance(flow, Data) for flow in flows] + flow_types = [type(flow) for flow in flows] + assert all(are_flows), f"Expected all flows to be Data objects, got {flow_types}" diff --git a/src/backend/tests/unit/test_logger.py b/src/backend/tests/unit/test_logger.py index 34414fc617f8..e86c703d23cd 100644 --- a/src/backend/tests/unit/test_logger.py +++ b/src/backend/tests/unit/test_logger.py @@ -2,7 +2,7 @@ import os import json from unittest.mock import patch -from langflow.utils.logger import SizedLogBuffer +from langflow.logging.logger import SizedLogBuffer @pytest.fixture From d0484ba41212f259b40a01e9fc9abafc75695c5b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 11:15:35 -0300 Subject: [PATCH 11/38] fix: enhance error handling in build_flow and add error handling for Flow build (#3259) * feat: add error handling for Flow build The code changes in `buildUtils.ts` add error handling for the Flow build process. It includes a new case for handling errors in the switch statement, which displays the error message and triggers the `onBuildError` function. This change ensures that errors during the Flow build are properly handled and the build process is stopped. * feat: enhance error handling in build_flow to capture and report HTTP exceptions in the flow building process --- src/backend/base/langflow/api/v1/chat.py | 15 ++++++++++++++- src/frontend/src/utils/buildUtils.ts | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 143937d1d146..49bf23410dfc 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -356,10 +356,23 @@ async def event_generator(queue: asyncio.Queue, client_consumed_queue: asyncio.Q except asyncio.CancelledError: vertices_task.cancel() return + except Exception as e: + if isinstance(e, HTTPException): + send_event("error", {"error": str(e.detail), "statusCode": e.status_code}, queue) + raise e + send_event("error", {"error": str(e)}, queue) + raise e ids, vertices_to_run, graph = vertices_task.result() else: - ids, vertices_to_run, graph = await build_graph_and_get_order() + try: + ids, vertices_to_run, graph = await build_graph_and_get_order() + except Exception as e: + if isinstance(e, HTTPException): + send_event("error", {"error": str(e.detail), "statusCode": e.status_code}, queue) + raise e + send_event("error", {"error": str(e)}, queue) + raise e send_event("vertices_sorted", {"ids": ids, "to_run": vertices_to_run}, queue) await client_consumed_queue.get() diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 969138598ee4..77dbb8054e75 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -264,6 +264,14 @@ export async function buildFlowVertices({ useFlowStore.getState().setIsBuilding(false); return true; } + case "error": { + const errorMessage = data.error; + console.log(data); + onBuildError!("Error Running Flow", [errorMessage], []); + buildResults.push(false); + useFlowStore.getState().setIsBuilding(false); + return true; + } default: return true; } From b0f80e463834d716ad221a8229c871e88bb3baf8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 11:46:15 -0300 Subject: [PATCH 12/38] feat: add log_builds parameter to build_flow for optional vertex build logging (#3262) --- src/backend/base/langflow/api/v1/chat.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 49bf23410dfc..2156b62b9ba7 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -146,6 +146,7 @@ async def build_flow( files: Optional[list[str]] = None, stop_component_id: Optional[str] = None, start_component_id: Optional[str] = None, + log_builds: Optional[bool] = True, chat_service: "ChatService" = Depends(get_chat_service), current_user=Depends(get_current_active_user), telemetry_service: "TelemetryService" = Depends(get_telemetry_service), @@ -250,7 +251,7 @@ async def _build_vertex(vertex_id: str, graph: "Graph") -> VertexBuildResponse: result_data_response.message = artifacts # Log the vertex build - if not vertex.will_stream: + if not vertex.will_stream and log_builds: background_tasks.add_task( log_vertex_build, flow_id=flow_id_str, From 5ce8cbda9b65cdc37380da4cc5b05f0da56b4004 Mon Sep 17 00:00:00 2001 From: goliath-yamon <141193714+goliath-yamon@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:30:23 +0100 Subject: [PATCH 13/38] Refactored and improved PythonCodeStructuredTool, SearXNGTool, and RunnableExecutor (#3239) * enhancement: Update PythonCodeStructuredTool to create inputs automatically and accept global variables * [autofix.ci] apply automated fixes * feat: Create a tool to search using SearXNG * [autofix.ci] apply automated fixes * refactor: reorganize imports and type annotations in PythonCodeStructuredTool.py for clarity and consistency * refactor: clean up imports and enhance type annotations in SearXNGTool.py for improved readability and type safety * refactor: Improved PythonCodeStructuredTool to allow arguments to have any types * refactor: Formatted and refactored SearXNGTool * refactor: Allowed RunnableExecutor to stream output and changed its build method to asynchronous. --------- Co-authored-by: Haseong Kim Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida --- .../components/prototypes/RunnableExecutor.py | 25 +- .../tools/PythonCodeStructuredTool.py | 358 ++++++++++++++---- .../langflow/components/tools/SearXNGTool.py | 141 +++++++ .../langflow/components/tools/__init__.py | 2 + 4 files changed, 450 insertions(+), 76 deletions(-) create mode 100644 src/backend/base/langflow/components/tools/SearXNGTool.py diff --git a/src/backend/base/langflow/components/prototypes/RunnableExecutor.py b/src/backend/base/langflow/components/prototypes/RunnableExecutor.py index 8aec1886f95c..0e872080afbf 100644 --- a/src/backend/base/langflow/components/prototypes/RunnableExecutor.py +++ b/src/backend/base/langflow/components/prototypes/RunnableExecutor.py @@ -1,7 +1,8 @@ from langflow.custom import Component -from langflow.inputs import HandleInput, MessageTextInput +from langflow.inputs import HandleInput, MessageTextInput, BoolInput from langflow.schema.message import Message from langflow.template import Output +from langchain.agents import AgentExecutor class RunnableExecComponent(Component): @@ -30,6 +31,11 @@ class RunnableExecComponent(Component): value="output", advanced=True, ), + BoolInput( + name="use_stream", + display_name="Stream", + value=False, + ), ] outputs = [ @@ -108,11 +114,24 @@ def get_input_dict(self, runnable, input_key, input_value): status = f"Warning: The input key is not '{input_key}'. The input key is '{runnable.input_keys}'." return input_dict, status - def build_executor(self) -> Message: + async def build_executor(self) -> Message: input_dict, status = self.get_input_dict(self.runnable, self.input_key, self.input_value) - result = self.runnable.invoke(input_dict) + if not isinstance(self.runnable, AgentExecutor): + raise ValueError("The runnable must be an AgentExecutor") + + if self.use_stream: + return self.astream_events(input_dict) + else: + result = await self.runnable.ainvoke(input_dict) result_value, _status = self.get_output(result, self.input_key, self.output_key) status += _status status += f"\n\nOutput: {result_value}\n\nRaw Output: {result}" self.status = status return result_value + + async def astream_events(self, input): + async for event in self.runnable.astream_events(input, version="v1"): + if event.get("event") != "on_chat_model_stream": + continue + + yield event.get("data").get("chunk") diff --git a/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py b/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py index 3d70785458ce..0a0e35ae3289 100644 --- a/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py +++ b/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py @@ -1,95 +1,307 @@ import ast -from typing import Any, Dict, List, Optional +import json +from typing import Any from langchain.agents import Tool +from langflow.base.langchain_utilities.model import LCToolComponent +from langflow.inputs.inputs import MultilineInput, MessageTextInput, BoolInput, DropdownInput, HandleInput, FieldTypes from langchain_core.tools import StructuredTool +from langflow.io import Output -from langflow.custom import CustomComponent from langflow.schema.dotdict import dotdict +from langflow.schema import Data +from pydantic.v1 import Field, create_model +from pydantic.v1.fields import Undefined -class PythonCodeStructuredTool(CustomComponent): - display_name = "PythonCodeTool" + +class PythonCodeStructuredTool(LCToolComponent): + DEFAULT_KEYS = [ + "code", + "_type", + "text_key", + "tool_code", + "tool_name", + "tool_description", + "return_direct", + "tool_function", + "global_variables", + "_classes", + "_functions", + ] + display_name = "Python Code Structured Tool" description = "structuredtool dataclass code to tool" documentation = "https://python.langchain.com/docs/modules/tools/custom_tools/#structuredtool-dataclass" name = "PythonCodeStructuredTool" icon = "🐍" - field_order = ["name", "description", "tool_code", "return_direct", "tool_function", "tool_class"] - - def build_config(self) -> Dict[str, Any]: - return { - "tool_code": { - "display_name": "Tool Code", - "info": "Enter the dataclass code.", - "placeholder": "def my_function(args):\n pass", - "multiline": True, - "refresh_button": True, - "field_type": "code", - }, - "name": { - "display_name": "Tool Name", - "info": "Enter the name of the tool.", - }, - "description": { - "display_name": "Description", - "info": "Provide a brief description of what the tool does.", - }, - "return_direct": { - "display_name": "Return Directly", - "info": "Should the tool return the function output directly?", - }, - "tool_function": { - "display_name": "Tool Function", - "info": "Select the function for additional expressions.", - "options": [], - "refresh_button": True, - }, - "tool_class": { - "display_name": "Tool Class", - "info": "Select the class for additional expressions.", - "options": [], - "refresh_button": True, - "required": False, - }, - } - - def parse_source_name(self, code: str) -> Dict: - parsed_code = ast.parse(code) - class_names = [node.name for node in parsed_code.body if isinstance(node, ast.ClassDef)] - function_names = [node.name for node in parsed_code.body if isinstance(node, ast.FunctionDef)] - return {"class": class_names, "function": function_names} + field_order = ["name", "description", "tool_code", "return_direct", "tool_function"] + + inputs = [ + MultilineInput( + name="tool_code", + display_name="Tool Code", + info="Enter the dataclass code.", + placeholder="def my_function(args):\n pass", + required=True, + real_time_refresh=True, + refresh_button=True, + ), + MessageTextInput(name="tool_name", display_name="Tool Name", info="Enter the name of the tool.", required=True), + MessageTextInput( + name="tool_description", + display_name="Description", + info="Enter the description of the tool.", + required=True, + ), + BoolInput( + name="return_direct", + display_name="Return Directly", + info="Should the tool return the function output directly?", + ), + DropdownInput( + name="tool_function", + display_name="Tool Function", + info="Select the function for additional expressions.", + options=[], + required=True, + real_time_refresh=True, + refresh_button=True, + ), + HandleInput( + name="global_variables", + display_name="Global Variables", + info="Enter the global variables or Create Data Component.", + input_types=["Data"], + field_type=FieldTypes.DICT, + is_list=True, + ), + MessageTextInput(name="_classes", display_name="Classes", advanced=True), + MessageTextInput(name="_functions", display_name="Functions", advanced=True), + ] + + outputs = [ + Output(display_name="Tool", name="result_tool", method="build_tool"), + ] def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict: - if field_name == "tool_code" or field_name == "tool_function" or field_name == "tool_class": - try: - names = self.parse_source_name(build_config.tool_code.value) - build_config.tool_class.options = names["class"] - build_config.tool_function.options = names["function"] - except Exception as e: - self.status = f"Failed to extract class names: {str(e)}" - build_config.tool_class.options = ["Failed to parse", str(e)] - build_config.tool_function.options = [] + if field_name is None: + return build_config + + if field_name != "tool_code" and field_name != "tool_function": + return build_config + + try: + named_functions = {} + [classes, functions] = self._parse_code(build_config["tool_code"]["value"]) + existing_fields = {} + if len(build_config) > len(self.DEFAULT_KEYS): + for key in build_config.copy(): + if key not in self.DEFAULT_KEYS: + existing_fields[key] = build_config.pop(key) + + names = [] + for func in functions: + named_functions[func["name"]] = func + names.append(func["name"]) + + for arg in func["args"]: + field_name = f"{func['name']}|{arg['name']}" + if field_name in existing_fields: + build_config[field_name] = existing_fields[field_name] + continue + + field = MessageTextInput( + display_name=f"{arg['name']}: Description", + name=field_name, + info=f"Enter the description for {arg['name']}", + required=True, + ) + build_config[field_name] = field.to_dict() + build_config["_functions"]["value"] = json.dumps(named_functions) + build_config["_classes"]["value"] = json.dumps(classes) + build_config["tool_function"]["options"] = names + except Exception as e: + self.status = f"Failed to extract names: {str(e)}" + build_config["tool_function"]["options"] = ["Failed to parse", str(e)] return build_config - async def build( - self, - tool_code: str, - name: str, - description: str, - tool_function: List[str], - return_direct: bool, - tool_class: Optional[List[str]] = None, - ) -> Tool: - local_namespace = {} # type: ignore - exec(tool_code, globals(), local_namespace) + async def build_tool(self) -> Tool: + _local_namespace = {} # type: ignore + modules = self._find_imports(self.tool_code) + import_code = "" + for module in modules["imports"]: + import_code += f"global {module}\nimport {module}\n" + for from_module in modules["from_imports"]: + for alias in from_module.names: + import_code += f"global {alias.name}\n" + import_code += ( + f"from {from_module.module} import {', '.join([alias.name for alias in from_module.names])}\n" + ) + exec(import_code, globals()) + exec(self.tool_code, globals(), _local_namespace) + + class PythonCodeToolFunc: + params: dict = {} + + def run(**kwargs): + for key in kwargs: + if key not in PythonCodeToolFunc.params: + PythonCodeToolFunc.params[key] = kwargs[key] + return _local_namespace[self.tool_function](**PythonCodeToolFunc.params) + + _globals = globals() + _local = {} # type: ignore + _local[self.tool_function] = PythonCodeToolFunc + _globals.update(_local) + + if isinstance(self.global_variables, list): + for data in self.global_variables: + if isinstance(data, Data): + _globals.update(data.data) + elif isinstance(self.global_variables, dict): + _globals.update(self.global_variables) + + classes = json.loads(self._attributes["_classes"]) + for class_dict in classes: + exec("\n".join(class_dict["code"]), _globals) + + named_functions = json.loads(self._attributes["_functions"]) + schema_fields = {} + + for attr in self._attributes: + if attr in self.DEFAULT_KEYS: + continue + + func_name = attr.split("|")[0] + field_name = attr.split("|")[1] + func_arg = self._find_arg(named_functions, func_name, field_name) + if func_arg is None: + raise Exception(f"Failed to find arg: {field_name}") + + field_annotation = func_arg["annotation"] + field_description = self._get_value(self._attributes[attr], str) - func = local_namespace[tool_function] - _class = None + if field_annotation: + exec(f"temp_annotation_type = {field_annotation}", _globals) + schema_annotation = _globals["temp_annotation_type"] + else: + schema_annotation = Any + schema_fields[field_name] = ( + schema_annotation, + Field( + default=func_arg["default"] if "default" in func_arg else Undefined, description=field_description + ), + ) - if tool_class: - _class = local_namespace[tool_class] + if "temp_annotation_type" in _globals: + _globals.pop("temp_annotation_type") + + PythonCodeToolSchema = None + if schema_fields: + PythonCodeToolSchema = create_model("PythonCodeToolSchema", **schema_fields) # type: ignore tool = StructuredTool.from_function( - func=func, args_schema=_class, name=name, description=description, return_direct=return_direct + func=_local[self.tool_function].run, + args_schema=PythonCodeToolSchema, + name=self.tool_name, + description=self.tool_description, + return_direct=self.return_direct, ) return tool # type: ignore + + def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict): + """ + This function is called after the code validation is done. + """ + frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node) + frontend_node["template"] = self.update_build_config( + frontend_node["template"], frontend_node["template"]["tool_code"]["value"], "tool_code" + ) + frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node) + for key in frontend_node["template"]: + if key in self.DEFAULT_KEYS: + continue + frontend_node["template"] = self.update_build_config( + frontend_node["template"], frontend_node["template"][key]["value"], key + ) + frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node) + return frontend_node + + def _parse_code(self, code: str) -> tuple[list[dict], list[dict]]: + parsed_code = ast.parse(code) + lines = code.split("\n") + classes = [] + functions = [] + for node in parsed_code.body: + if isinstance(node, ast.ClassDef): + class_lines = lines[node.lineno - 1 : node.end_lineno] + class_lines[-1] = class_lines[-1][: node.end_col_offset] + class_lines[0] = class_lines[0][node.col_offset :] + classes.append( + { + "name": node.name, + "code": class_lines, + } + ) + continue + + if not isinstance(node, ast.FunctionDef): + continue + + func = {"name": node.name, "args": []} + for arg in node.args.args: + if arg.lineno != arg.end_lineno: + raise Exception("Multiline arguments are not supported") + + func_arg = { + "name": arg.arg, + "annotation": None, + } + + for default in node.args.defaults: + if ( + arg.lineno > default.lineno + or arg.col_offset > default.col_offset + or arg.end_lineno < default.end_lineno + or arg.end_col_offset < default.end_col_offset + ): + continue + + if isinstance(default, ast.Name): + func_arg["default"] = default.id + elif isinstance(default, ast.Constant): + func_arg["default"] = default.value + + if arg.annotation: + annotation_line = lines[arg.annotation.lineno - 1] + annotation_line = annotation_line[: arg.annotation.end_col_offset] + annotation_line = annotation_line[arg.annotation.col_offset :] + func_arg["annotation"] = annotation_line + if func_arg["annotation"].count("=") > 0: + func_arg["annotation"] = "=".join(func_arg["annotation"].split("=")[:-1]).strip() + + func["args"].append(func_arg) + functions.append(func) + + return classes, functions + + def _find_imports(self, code: str) -> dotdict: + imports = [] + from_imports = [] + parsed_code = ast.parse(code) + for node in parsed_code.body: + if isinstance(node, ast.Import): + for alias in node.names: + imports.append(alias.name) + elif isinstance(node, ast.ImportFrom): + from_imports.append(node) + return {"imports": imports, "from_imports": from_imports} + + def _get_value(self, value: Any, annotation: Any) -> Any: + return value if isinstance(value, annotation) else value["value"] + + def _find_arg(self, named_functions: dict, func_name: str, arg_name: str) -> dict | None: + for arg in named_functions[func_name]["args"]: + if arg["name"] == arg_name: + return arg + return None diff --git a/src/backend/base/langflow/components/tools/SearXNGTool.py b/src/backend/base/langflow/components/tools/SearXNGTool.py new file mode 100644 index 000000000000..3749a70da529 --- /dev/null +++ b/src/backend/base/langflow/components/tools/SearXNGTool.py @@ -0,0 +1,141 @@ +from typing import Any +import requests +import json + +from pydantic.v1 import Field, create_model + +from langchain.agents import Tool +from langflow.base.langchain_utilities.model import LCToolComponent +from langflow.inputs import MessageTextInput, MultiselectInput, DropdownInput, IntInput +from langflow.schema.dotdict import dotdict +from langflow.io import Output + + +class SearXNGToolComponent(LCToolComponent): + search_headers: dict = {} + display_name = "SearXNG Search Tool" + description = "A component that searches for tools using SearXNG." + name = "SearXNGTool" + + inputs = [ + MessageTextInput( + name="url", + display_name="URL", + value="http://localhost", + required=True, + refresh_button=True, + ), + IntInput( + name="max_results", + display_name="Max Results", + value=10, + required=True, + ), + MultiselectInput( + name="categories", + display_name="Categories", + options=[], + value=[], + ), + DropdownInput( + name="language", + display_name="Language", + options=[], + ), + ] + + outputs = [ + Output(display_name="Tool", name="result_tool", method="build_tool"), + ] + + def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict: + if field_name is None: + return build_config + + if field_name != "url": + return build_config + + try: + url = f"{field_value}/config" + + response = requests.get(url=url, headers=self.search_headers.copy()) + data = None + if response.headers.get("Content-Encoding") == "zstd": + data = json.loads(response.content) + else: + data = response.json() + build_config["categories"]["options"] = data["categories"].copy() + for selected_category in build_config["categories"]["value"]: + if selected_category not in build_config["categories"]["options"]: + build_config["categories"]["value"].remove(selected_category) + languages = [] + for language in data["locales"]: + languages.append(language) + build_config["language"]["options"] = languages.copy() + except Exception as e: + self.status = f"Failed to extract names: {str(e)}" + build_config["categories"]["options"] = ["Failed to parse", str(e)] + return build_config + + def build_tool(self) -> Tool: + class SearxSearch: + _url: str = "" + _categories: list[str] = [] + _language: str = "" + _headers: dict = {} + _max_results: int = 10 + + @staticmethod + def search(query: str, categories: list[str] = []) -> list: + if not SearxSearch._categories and not categories: + raise ValueError("No categories provided.") + all_categories = SearxSearch._categories + list(set(categories) - set(SearxSearch._categories)) + try: + url = f"{SearxSearch._url}/" + headers = SearxSearch._headers.copy() + response = requests.get( + url=url, + headers=headers, + params={ + "q": query, + "categories": ",".join(all_categories), + "language": SearxSearch._language, + "format": "json", + }, + ).json() + + results = [] + num_results = min(SearxSearch._max_results, len(response["results"])) + for i in range(num_results): + results.append(response["results"][i]) + return results + except Exception as e: + return [f"Failed to search: {str(e)}"] + + SearxSearch._url = self.url + SearxSearch._categories = self.categories.copy() + SearxSearch._language = self.language + SearxSearch._headers = self.search_headers.copy() + SearxSearch._max_results = self.max_results + + _globals = globals() + _local = {} + _local["SearxSearch"] = SearxSearch + _globals.update(_local) + + schema_fields = { + "query": (str, Field(..., description="The query to search for.")), + "categories": (list[str], Field(default=[], description="The categories to search in.")), + } + + SearxSearchSchema = create_model("SearxSearchSchema", **schema_fields) # type: ignore + + tool = Tool.from_function( + func=_local["SearxSearch"].search, + args_schema=SearxSearchSchema, + name="searxng_search_tool", + description="A tool that searches for tools using SearXNG.\nThe available categories are: " + + ", ".join(self.categories), + ) + self.status = tool + return tool diff --git a/src/backend/base/langflow/components/tools/__init__.py b/src/backend/base/langflow/components/tools/__init__.py index 3ebec76ee83b..6c411d6301ac 100644 --- a/src/backend/base/langflow/components/tools/__init__.py +++ b/src/backend/base/langflow/components/tools/__init__.py @@ -5,6 +5,7 @@ from .GoogleSerperAPI import GoogleSerperAPIComponent from .PythonCodeStructuredTool import PythonCodeStructuredTool from .SearchAPI import SearchAPIComponent +from .SearXNGTool import SearXNGToolComponent from .SerpAPI import SerpAPIComponent from .WikipediaAPI import WikipediaAPIComponent from .WolframAlphaAPI import WolframAlphaAPIComponent @@ -18,6 +19,7 @@ "PythonCodeStructuredTool", "PythonREPLToolComponent", "SearchAPIComponent", + "SearXNGToolComponent", "SerpAPIComponent", "WikipediaAPIComponent", "WolframAlphaAPIComponent", From 98f00d75f0a503f5ae379156e27daac62a2b2898 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:46:17 -0300 Subject: [PATCH 14/38] Update README.md (#3204) * Update README.md Updated Readme with suggestions * Changed core features * Changed thumbnail of video * Fixed hero image * Fixed readme texts * Update README.md --- README.md | 194 +++++---------------------------------- docs/static/img/hero.png | Bin 124069 -> 156938 bytes 2 files changed, 25 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index a8676da6b3b9..7aa3e8c90c11 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,16 @@ -
-

Langflow 1.0 is OUT! 🎉

-

Read all about it here!

-
- # [![Langflow](./docs/static/img/hero.png)](https://www.langflow.org) -

- A visual framework for building multi-agent and RAG applications -

- Open-source, Python-powered, fully customizable, LLM and vector store agnostic + Langflow is a low-code app builder for RAG (retrieval augmented generation) and multi-agent AI applications. It’s Python-based and agnostic to any model, API, data source or database.

Docs - + Free Cloud Service - Join our Discord - - Follow us on X - - Live demo -

- -

- - - - - - + Follow us on X

@@ -39,168 +22,41 @@

- Your GIF -

- -# 📝 Content - -- [📝 Content](#-content) -- [📦 Get Started](#-get-started) -- [Running Langflow from a Cloned Repository](#running-langflow-from-a-cloned-repository) -- [🎨 Create Flows](#-create-flows) -- [Deploy](#deploy) - - [DataStax Langflow](#datastax-langflow) - - [Deploy Langflow on Hugging Face Spaces](#deploy-langflow-on-hugging-face-spaces) - - [Deploy Langflow on Google Cloud Platform](#deploy-langflow-on-google-cloud-platform) - - [Deploy on Railway](#deploy-on-railway) - - [Deploy on Render](#deploy-on-render) - - [Deploy on Kubernetes](#deploy-on-kubernetes) -- [🖥️ Command Line Interface (CLI)](#️-command-line-interface-cli) - - [Usage](#usage) - - [Environment Variables](#environment-variables) -- [👋 Contribute](#-contribute) -- [🌟 Contributors](#-contributors) -- [📄 License](#-license) - -# 📦 Get Started - -You can install Langflow with pip: - -```shell -# Make sure you have >=Python 3.10 installed on your system. -python -m pip install langflow -U -``` - -Then, run Langflow with: - -```shell -python -m langflow run -``` -# Running Langflow from a Cloned Repository +https://github.com/user-attachments/assets/a1a36011-6169-4804-87ad-cfd4c5a79872 -If you prefer to run Langflow from a cloned repository rather than installing it via pip, follow these steps: - -1. **Clone the Repository** - -First, clone the Langflow repository from GitHub: - -```shell -git clone https://github.com/langflow-ai/langflow.git -``` - -Navigate into the cloned directory: - -```shell -cd langflow -``` - -2. **Build and Install Dependencies** - -To build and install Langflow’s frontend and backend, use the following commands: - -```shell -make install_frontend && make build_frontend && make install_backend -``` - -3. **Run Langflow** - -Once the installation is complete, you can run Langflow with: - -```shell -poetry run python -m langflow run -``` - -# 🎨 Create Flows - -Creating flows with Langflow is easy. Simply drag components from the sidebar onto the workspace and connect them to start building your application. - -Explore by editing prompt parameters, grouping components into a single high-level component, and building your own Custom Components. - -Once you’re done, you can export your flow as a JSON file. - -Load the flow with: - -```python -from langflow.load import run_flow_from_json - -results = run_flow_from_json("path/to/flow.json", input_value="Hello, World!") -``` - -# Deploy - -## DataStax Langflow - -DataStax Langflow is a hosted version of Langflow integrated with [AstraDB](https://www.datastax.com/products/datastax-astra). Be up and running in minutes with no installation or setup required. [Sign up for free](https://langflow.datastax.com). - -## Deploy Langflow on Hugging Face Spaces - -You can also preview Langflow in [HuggingFace Spaces](https://huggingface.co/spaces/Langflow/Langflow). [Clone the space using this link](https://huggingface.co/spaces/Langflow/Langflow?duplicate=true) to create your own Langflow workspace in minutes. - -## Deploy Langflow on Google Cloud Platform - -Follow our step-by-step guide to deploy Langflow on Google Cloud Platform (GCP) using Google Cloud Shell. The guide is available in the [**Langflow in Google Cloud Platform**](./docs/docs/Deployment/deployment-gcp.md) document. - -Alternatively, click the **"Open in Cloud Shell"** button below to launch Google Cloud Shell, clone the Langflow repository, and start an **interactive tutorial** that will guide you through the process of setting up the necessary resources and deploying Langflow on your GCP project. - -[![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/langflow-ai/langflow&working_dir=scripts/gcp&shellonly=true&tutorial=walkthroughtutorial_spot.md) - -## Deploy on Railway - -Use this template to deploy Langflow 1.0 on Railway: - -[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/JMXEWp?referralCode=MnPSdg) - -## Deploy on Render +

- -Deploy to Render - +# Core features +1. Python-based and agnostic to models, APIs, data sources, or databases. +2. Visual IDE for drag-and-drop building and testing of workflows. +3. Playground to immediately test and iterate workflows with step-by-step control. +4. Multi-agent orchestration and conversation management and retrieval. +5. Free cloud service - get started in minutes with no setup. +6. Publish workflows as back-end APIs or export as a Python application. +7. Real time observability with LangSmith, LangFuse, or LangWatch integration. +8. Support enterprise security and scale with free DataStax Langflow cloud service. +9. Prebuilt ecosystem integrations to any model, API or database. -## Deploy on Kubernetes -Follow our step-by-step guide to deploy [Langflow on Kubernetes](./docs/docs/Deployment/deployment-kubernetes.md). +![Integrations](https://github.com/user-attachments/assets/df4a6714-60de-4a8b-aff0-982c5aa467e3) -# 🖥️ Command Line Interface (CLI) +# Stay up-to-date -Langflow provides a command-line interface (CLI) for easy management and configuration. +Star Langflow on GitHub to be instantly notified of new releases. -## Usage +![Star Langflow](https://github.com/user-attachments/assets/03168b17-a11d-4b2a-b0f7-c1cce69e5a2c) -You can run the Langflow using the following command: +# 📦 Quick Start +- **Install Langflow with pip** (Python version 3.10 or greater): ```shell -langflow run [OPTIONS] +python -m pip install langflow -U ``` -Each option is detailed below: - -- `--help`: Displays all available options. -- `--host`: Defines the host to bind the server to. Can be set using the `LANGFLOW_HOST` environment variable. The default is `127.0.0.1`. -- `--workers`: Sets the number of worker processes. Can be set using the `LANGFLOW_WORKERS` environment variable. The default is `1`. -- `--timeout`: Sets the worker timeout in seconds. The default is `60`. -- `--port`: Sets the port to listen on. Can be set using the `LANGFLOW_PORT` environment variable. The default is `7860`. -- `--env-file`: Specifies the path to the .env file containing environment variables. The default is `.env`. -- `--log-level`: Defines the logging level. Can be set using the `LANGFLOW_LOG_LEVEL` environment variable. The default is `critical`. -- `--components-path`: Specifies the path to the directory containing custom components. Can be set using the `LANGFLOW_COMPONENTS_PATH` environment variable. The default is `langflow/components`. -- `--log-file`: Specifies the path to the log file. Can be set using the `LANGFLOW_LOG_FILE` environment variable. The default is `logs/langflow.log`. -- `--cache`: Selects the type of cache to use. Options are `InMemoryCache` and `SQLiteCache`. Can be set using the `LANGFLOW_LANGCHAIN_CACHE` environment variable. The default is `SQLiteCache`. -- `--dev/--no-dev`: Toggles the development mode. The default is `no-dev`. -- `--path`: Specifies the path to the frontend directory containing build files. This option is for development purposes only. Can be set using the `LANGFLOW_FRONTEND_PATH` environment variable. -- `--open-browser/--no-open-browser`: Toggles the option to open the browser after starting the server. Can be set using the `LANGFLOW_OPEN_BROWSER` environment variable. The default is `open-browser`. -- `--remove-api-keys/--no-remove-api-keys`: Toggles the option to remove API keys from the projects saved in the database. Can be set using the `LANGFLOW_REMOVE_API_KEYS` environment variable. The default is `no-remove-api-keys`. -- `--install-completion [bash|zsh|fish|powershell|pwsh]`: Installs completion for the specified shell. -- `--show-completion [bash|zsh|fish|powershell|pwsh]`: Shows completion for the specified shell, allowing you to copy it or customize the installation. -- `--backend-only`: This parameter, with a default value of `False`, allows running only the backend server without the frontend. It can also be set using the `LANGFLOW_BACKEND_ONLY` environment variable. -- `--store`: This parameter, with a default value of `True`, enables the store features, use `--no-store` to deactivate it. It can be configured using the `LANGFLOW_STORE` environment variable. - -These parameters are important for users who need to customize the behavior of Langflow, especially in development or specialized deployment scenarios. - -### Environment Variables - -You can configure many of the CLI options using environment variables. These can be exported in your operating system or added to a `.env` file and loaded using the `--env-file` option. - -A sample `.env` file named `.env.example` is included with the project. Copy this file to a new file named `.env` and replace the example values with your actual settings. If you're setting values in both your OS and the `.env` file, the `.env` settings will take precedence. +- **Cloud:** DataStax Langflow is a hosted environment with zero setup. [Sign up for a free account.](http://langflow.datastax.com) +- **Self-managed:** Run Langflow in your environment. [Install Langflow](https://docs.langflow.org/getting-started-installation) to run a local Langflow server, and then use the [Quickstart](https://docs.langflow.org/getting-started-quickstart) guide to create and execute a flow. +- **Hugging Face:** [Clone the space using this link](https://huggingface.co/spaces/Langflow/Langflow?duplicate=true) to create a Langflow workspace. # 👋 Contribute diff --git a/docs/static/img/hero.png b/docs/static/img/hero.png index 85bc2731dfb6c26354e8a53b1f0b87291a5d9706..5eff7db0dd54d0c23dce6403c2273efe69b03fdb 100644 GIT binary patch literal 156938 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⪚!EPMO(2s|1gLCm$o4@uFs$FPF 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≷BegM84pP?;qd zPx+#*nQl+qE7EsBfqg^VuJ6uk7xMVzPMT;<8Hj3*bC>nBCqx%0{!Nk zQmf2%q$>HGf(|25u=l>ny!!F4VkV%7c2;3MxmF%6sN?leVQui5|HbvEMDNU+d%9qR z4FbPZ5P1|C>eKlseDBjX%6xR1BYZ(z1~Dv@1hP(s1BuDE>pxpg<1Rw9Jsc{y#+fu; z(t1@uuig*;z{gYZ<645Sv1OtkAe%;VsTShEsg7u=lPEW^mPTmH} zb0ek`K$C#7gX7O5_x}J#2MTQQ5%AnJp)>#V%dj(ZQZlGG_t`z5CzC{cDH5Q4u^-o} z;2l0XbBWU9PVQG7S^mA=9s&`+rf8=@v)3wFcW0)^0r4c$5P{y`w`yQ6^23oEAae?! zz-(wjDUs|V?`Av=TY$F7*(68^70(9mxN9rHQd(smru|kXm3qbaudb9T%T~Oz4|c>0 zDRXvwMm*wIUmYMG8Qj$&xPmW(c}HH<%0fpX-t2gE%9oh$K6J=1$CZ1tt^$L{BvIeW zjJ6YpYJFLE&LAb)8sj#ZSgI5#gowuqJeOa<|3--UEoBIjD4oiM064F`i4PwlLOaLM zn){K)G@gXSA-wgzC|@0~;rYmN;m(&GMzwnX(v6*E%~^gjXyHtp1w}*U{>`#oDh6sJ zm4UL~1l|0Wo_wUVL=*bu%;Wh>Q-pIt(n->Ws9s{ z4}rU{f9kmXqtOAGGgU;yEqFV!W_nrH{Sgsxdlti%)CLZD-l}miZS8nmxjX>=SZ^Cve)PDc z-^29g*k4O=zc^%Z-Ue9%yevr`Qg~9&&)qcKUtZ@uvJ)V)C-oz8a)q!M(I1zEb>=SpdGSz2i_{3IK(E^= zau#%)&ZV)}eSfZtJoTj3KO!4x23es#XJ4l60zu>93b1=hrzxj#60@d3FKwi}niE80 zR81z^{eHhuMaj0|<0UuzFcFnKs_4A#9IkQ7Xtww3Ol1Di>h#HZEb&A>ar>)dvuISP zX~Aw3#87t2+ec!Ta{TIhQAfcQOuhNdnB!ekIqLb&c|&vC^!3`K#k)s-bh>%Qd0+Og z0u3`Pr)MDXl8t6Ub(by_HgLii?f$`#V8>4&E znU~51@I72CfT)IhGUBwO-db#t8jEUgVzd^1=I@9}2CDE7l5{m|F5Alq{Mq?pph>C$ zukp*7_c{)zLXL$^Gb(SuTW-2npHu5%JNu7E4qGKave?e-oBdCW(nS)aS^$@^EUSG# z_1mU5OA#P6v3#%6l`FMYL5Nu4s;R39`6Hg6yL=aHvc%-xA%dD_{(7F7JRMzJNDQd2 z9Q?HxHWi@2B<^gRsD=cT3i(iAScn7=uXM{qzb50}7jT&GktwqM1weFP=NR9+6c6VkJ_Swi&_GezHXe(rAv#!>OLA$7^@D z(@ONluwysezg)QcQOHlHNj_PQaocpI!BW?ksEre=klMni5<#U7D-%w;8uo#FC%E;8 zu{|C;XZ3kdF}T08l`hA8l_i8c*}1MJ+}M0}mZ47vW}5#~kchoB{6YB739hwI4~EQN z4Jx-m9a8lVjNWv!W)l)q5sj|8AQaT+&`lJG4k8Rt;?54viTbt9@Z)@HQHN`Hp>k_hAGF2ez{`V> zp+3YO{FX7+&V2fctFi9|N>lNsg7xh@zv+(IUj;&6UKM^ zxTaXTt!9-t@m_oucFD+fhu!&A*(sQKrv(Ri$b#&LSt6dLwzL5SkS%|sJOb$ZR&7!M zgx^kxt^SWLjr;>#UqFT?vtZ70n>IXoFq+-?@mZlq_e~@ywil+tLNUt{@LP!Av~_BT z`yE3lJmE=-&+AWmhSoWTU-jTps7DriL%L}M8 z)4YP!7rx}=X8@FzIP|G5RR1iR{~V4m8!ZohPx)%TNTd8Qw$TR3vmwc=6_r%{+6RXq z?-24cns1^Dbp0tDIf>ls5@gPMHQW!e?**R+fu5Ex&ViWy zwV5w2bOd&oSSy*;G<`WJ(0X>~HQ!WOlYdDttG&?*?U-C!Hh!q;Gd^?`%_zopT4Q#` z(MRgOG{EpUM0jrQn7x5VlPy&Ys!tT zEJrDR?$N_2@kGRi&Ny0oB=RMDsqp@dnuvw4F|Nkm3nyPb4Kowd^V;Gjfu^|L;6ze| z=u(y#^@e=cdCs45j^;Aamk&I1W^>;EH91nmfeDq(DU#!-g@%6?pWY_>RJrX?kb~mV zi4m6ifY45UcjFdmAU!%m?e-yE8cX*WP@PjR{0NNNl~{NQiNEKD$NezhJ`Gph*S}LY z{K_+Q^%*cT5djd`qDI?YaU>hK)ERsFRPQ~l_cLVje{H6L+oMsY|LA(vRD7y3twMGT zCBM&|!7N$@s6X!^6Za4Tk?PaS*qs)2Zs=Vg{RXqNjZ?Jkt__90Lu(G+JBlT57j=cHcS>5c1ZRQ z%)QsZ7Pt#An(dtxvA*r%vb7ZS58b$6E%NfYH7cp^wBbt{)ghp zjZ*AXUs+6A)^2|!WK^i%_I0H$(BOS?+J|S1We`YXiD!gmz-AtlQ&74}%qI+zgjC1t z<0WMIY3hk=Tx<$q+A+~`>}D@?PbtuK1ynGpky$U;y%q5F&1`&-l%NmrFCKm;3cZW2 zvMIhRFCKjSl?7c?80~xhI*aZuOx*%{Eb@(_!LS~zdy3KFzxcE3?S=(qVE^VCMLlC2 zcg{o3wYsqDTYc!wi8no6f3~=>AbWN3dB2lB#+8SHTyuQhW{`5GN~K9HSpgJ|q@xek zmBP*GQEVy)6MFUJ}djXsRGRS*->t(uEc z)~9j(a6Gznj$3T=E&g-RzCP8A$l#STb=)fLLE-w>*h@|gVp=1)pVX>avdpxH?w@DT)fo4b9YFf+DMfg&U_i$$uL zm&=cdX&w!e#r?3%o7WSAPaaJ|0D5P}l_{#)=-ImRbD^6qlQPGo3t2G+q+-(Z)p|ib z{J-8(i2T`c*?N{NoE+oNj+=uyfIy>5y*mQYByWG$8#KOFX~pNJtrm#=#3B;^wd($5 z#(}M2`-Ck!sAePrFaD=mVJye{usQhn#AkQ8v*Yn7u|-d?PCFS=lsOT(<20c} zOkZb=T+d!}SgACX2ek6oiL7b~g1Y^9RA4DV(?ic~@JL_cVF$stCm(ejtEtn21j|5< zWen|)@+LLNFfamWBa4!b8McAGn`U}?*Cn6kiY79ttKs>TN5@J$+XmmHoz+Br!gEg5 z(*!FNLVUHYCtqnORi=(&W&sL%>8F*0=wflD$#=QqccGiC=&>4xHficQXBob3Q zTuONsUfr5lhn(xlH7P`K?ZX9H<$+z$IfD0U>E4F@d!KD8bvvID_}NWS@9EfJkoM~< zWd`Af@JA-Thodx)JrwlEI+eV7jR&;5hl+PC4`VkfJ4SX|lunHOQMI6Yl1>cN(6BqT zMgO^r@>buo@p)>$r8Vk0-CPIg)hCjhug6-%S^-9QS^{Mpz9ZY(LGa(P)j+=A+rZhr zC-@F1K^xwFEUzO8cn@mLy5k24g-gmF+3&k8kiJ22kn$lAMoulay^lRczNI$Q)VZfe zL&UYnZyOjWoq6(`)Vs2}d^XoS*vs<{Z36YP=ipzKDeu*uUlHBE@<pZVi;n<9LvN^lNxtr7d{W{{Vzbt#Vbu9XcS64D17E_zQo@Kpy7+U0WM%<% z$?Dhb%6AU*SQ539e1|f`0oOvBJF5=T#PG|`;DbnR4)paf_C`Ka9^J+0Fm%X`a8TZui1iAiF-o%Ii zkl|sS!jF<2rTnOI4^P^>vE}Qz#Z*Y36qfrM&bPou-C@A&9|h0#v((WI?`{whq{F#2 zJb!k!OSZVF?75~MC61lWd3!m?Gg5UQ%aWA)5BCmeY{aMM!$eJ7t81*;S>H#cTeR?q zy!pav_xdWIm{Y~&RX}xIq!{gUbk%(W7hZ1P>5aS3UY)KLfH097(RZmuwN!}n{M1S= z56L#-;C!cIN=l-%RVn}U3V)r$-&;ymWYWVG?D9t;U-be_?59CSmWHGZ+ z4nfr&*F@In&gGYf4+N}MZ{}SX>GQ#j*(VuROx7L6BSp+8O$&mjj=a?${<^ebqFyX0 zeT-)E<6q&$cUE7?AliXW-mWEs5t2;_M<+*feFXIH-%S${toUDX^*np-?biP_Sy)L( zejBLi3EL{eL?D7hr7fEOSlYi2_wTqk$*u{DZO(DS!8}!`9~Z@`U~JbT!-Ijs4QM1$@6RBDh2@FxVWJxB zr7q+~PZbXOkvjdfcV923M=1QK^VIA{Rv8WI(b5xnltl%lN?rsjOL6fr>B+*5j^q?*5hrTf`q$#XLDXPZ-1+y1wi7i04x+N4 zG+6;IauxSx&x*~pIRZ}9UA?zJlQ}9yw(D;kfeOL5#-Nc+%+Z91%oPOlG&(vsR{Sf) z$9FIHXvm;cu=f*Fd8Wx}X=&Ykx=2Ny))|r=qM6+Q*vw`vH^o29+Z5*0HH~7T|X}* z<+SXDuI@bT?Dzk*_%5PEJ3OEh&Gv!M9iU7Mk-J^K?VFx8|6~z>41M)H0%Y&=nI`!W z2tkE~2DXs}rVC<1nLjBP;p4mKve?)|p~4(?eJu^t!Do$XeI1>@fszrK0Gu&p;>Eg(VqAxK-Y34%p3_TbddGf$i5E z?qzBe{kv8vKF6|)py$g}AO3#h`hQohjK{&L(O3dIu@F~g!bNBSGC2s}-O9PMS+Qsa zF(y<0MJAqZq{GbTw;0V*UUi)bJ+(3-6e=r<00YbzTwA6yk5rBj>$X^Iw*H3jGP*dO z4bt_uO?i|m^Om+uU+VrmcT>NqYeyP+;>eF5@&?mob7z~`dBatJ{F;oisIt#QR5Z+GUEhjnWLbGmuJkncuu6VB55Y{w!gok|HZjyrP*b{PjuFEZ@KF^Iz7= zR{MmWGW0Wqj+=)AQ}q$)w+9d3Cym7pq?$`osQ+l zTK(t>`a2}7YHI?IEc$*>yAMX$JXD3now^GC$D6oqlf?~<(nx0$lp|xDsf>M{GD|2v zD|ufIJ^yJYiP!SIzije#JLB@c$64dE?@_SbC7*+VLBX|%{SG=4Z9N}NO;VFa#~97c z%!mk9CXjQ;Ij|)`>olqlyUn-^^nvH@(|t}<8PFnaMc>1M{y7So{l^Z-4^RX4}wwXi*h6{1}>EnB=?2ASd6zH^#fn57)^SK&emQkwc zU=)|@Qk%pG8bKc`bN!fzh-mCj0cp2YnC0cwF`eZenOx+odspW5h-s5kSb~;}exk+M zg%1C%Z`wnPMkoERGqoUs8rOve3f6!V3Xj`*Mzg95GGG@%Vr9>@PeKBHyiFKnjq7mr z%k;j{+h=|3(;)3%mhxCZHZS!7gE?@CT^E}K0PfVs8J9&4`zw#3bRjB0m@vfyOZ-vx zNBo^>#mS>I&&yG!w$?q@>qgh?{NEp$7xInktsifM(J2NniaABLdwH#vhBcggKmep3 z&$lzqzE^wA_qF}CL@_Ldbl*+O90bMJ@ z#T>-TRwo>9n(4QJ@bA~iIWsIY^&O*U5N&Zk$Q{c5hS0lavZ&iox=pWO9hv5Muudx9NCCmYj_laO)F zl$`P0h$w<<2GO^ubzRMGHz1W9Fw%SLblJ4lcV77VU=MmeJ8%C{sO_m&l!xO`>Qj=2*1i)+6X7wHzU-=YXzho*SRy^{xk87#>Pe1yNle= z4=bwUcA3g8unlb!rfko%)5$FELwxBxH#5}kpKCUT?ygh)JAVCF+8l(Vffc;%-y}>8 zv$`voe!IhOt4hJ`dsABXV5XU3j-P<5mGVR%aI6mxYk^G)DQAvOF;5L{dH>5uG+v->GFbUW zh6SbTwb+Av_%>R}Ev`7U^u&bNA=74Js-l9QxHEtn%_`d+N8IrG;;2NsL)zo_T#yi% z9DmzS$Xejl+<3m*u|PWn(zx?uc$i{iFhwwSxbqL-1O%SV z0#_mMPq-G`+Y<{TXi(8rX!!cG3X8X@(dd}l^t7S%ewNJn>L)?ScYQ;m1Fbuq{ypJa zUdO}tr7Z(Dg@>ZQYjX;aEJ};frL(w&2A*59c*>tQWY6V>j9H!zB$XJnOM-vq$8~FZw)7@#?`~UZzqfIrB-`) zEjBX9m6n+Kg_&c{AchG=3|;N|7nhSQ80-dk&xi_E+PFrqJ)jgpl=l(>i z>2dQZ94BPIUF*&}Nz%8zsWo3H#<$fb^ypC?2RE4U{Mq5u^K+{6<5;;~iT10daD9vF zUct_5N_rUnw!*Q;Y2$3p>y~$^mq&xN>%}o2GrZ5bQY?JgrGH=ib&yjJ$+u9C{N`}@ zZHXPgV2!8%!Z}8Ar0?Kd(}!u0c|7JKFB&omUR3_DUO$>rs4Nj{0?X`Ow?!I>>jchX z{q2MNScx?gpT#63RE14cY2 zZf2=m5YfoXZi-m=(C{S-%zDFg8!nR!En?EDJ9zVY1>r|(Lz%~qI*dB7Wn5eEyu7@+ z>k2g%o4V7(^JaIM-bJdaULL96QW-pGsE)nb4Z8o>)a;ec8Y0p(xQ-pcK;X=iWzt zA6sEv@rSNn*N3bA?bn0VE+yzY=MG&+(!p9?HhI9oy_brvJ z@A4;e2Dr@)BZFBvHxM}dUbo@t`TMy6{s&-KHRN`n0%=?f1*SF8_4LR9k%v=ZXf^Hx z;}hpd#GXW|IP_7+En4a=6SJm-@dG|2+#V2p&yG#9p!eghk$u}oJXN88k6BZeu-^)(wq?z4-k=)jbmVf238Ra&Dfp|jKeSzKb#=D4`Z2I+es8SG zOw8QpRR4)&0|n6;jph7iHV6d5$sCcQ>t_y-_E`UAb=lxJceH2+3?E?J)0BSoz@D>4 z%m2jwtJ__>k<_@kuro|`o;9mYWS1%}2$ItJ8-M(FF85#G3eAFY9zwAY+h1gH zluO{E&eN2An(1ZuXc_ObUdLNWoaM?3gY(?MhYkT`(0mz{skT)V%Wc(7R7JCX*gC)M zlmwRy0a$n0ols9iL_}tJvXZ9k)B4cpV60EZPM#QId=+=Ao#RLQ2+!Vb;?*coqAebR z1zmtA^PD4ZaeEniEvx+5yY2o*`OC;VX7V0x9d8)92Cg1xkklnQ&XrK$VCvdby?*~+ zubd?xIRTQU3(U#o&Z$9TfRk0K71Q+N{dMps!EQmhKm3$noji@8ehpX4!}{q;PjSzA zEvyNR0Iyw9mkGVNQ_<~$(l&#CxSxlbc>F6_7e!com7yheiLLP}&rN!9aWD?RfU+?t zm!-adZ93~>aFCDNBT8__|LMY#vCYSj0MQ4@xgZeOoc?55egnJ$?s>nq{$3K==ROPV z0aPlZRQf(s-7-)d@xE?mpp<)hvoied4F}?0j7aINK*Gy(lRCR7pB~`EY&Ea|>m@;m zzS=3`hMV;f{Ze&O(KJtIrJ?FEau|Iou4RHD+^aoSAE`(}Fv3Cs|IOQiUQ5`pz zxqwP)>dCqyaLXTt7Dk7S3bYdxFk+o~IK%4&*4!Np69x)OIjtsORDf-27f}=Jp{lT< zgPUg7Jl0&_#8!M>dUj8;yVz7bDC2!?D2*VIVz~PH4oH`Lsx#9Lu~&RVE$Ey z`R|XQr+C4ZaB21t9px9Pyfw`#q41#Iy?l>MNfvZ;G7PMM{lnU&4)gJJI6BU7g-7pt|u^`(h^c$WvMih&PLMxw+d6%bGwxK*6_c)1H z|JkzCl@LVhltZfYxtPfl*YJu8UIQSTsQ$LfsUDY*k+Jg&Ci~9?23Z;8I2q8IG&pd1 z*Lwc6|Bhe;|22yL`~Wt3yz4Ibnhf8YvaWT5guX#<5qcUJ>xVG~7<5JciR?9`4TFZC zFDKG5cOik5HBhpO2wKGLOUNKH1GAQ_SF(n7R^Iz7*{jIk3VbQ`t30R=wz)v2<9L9%_4Ml5)EOh!MLHRqHZ z_I9pB_OlDPOG@(HyLWL3aGY|3O4RB5z!533@)KUjG);=-)B)B^dq7XP25hVDZH<3V z--Ogdw8VeEmbQ9W+iPeNq;eHMIf=jCbG%#O1BVw3{=`qm*fK`{<@?-i9wkLh*qx>lz zH(um(TSd4Qz~q>wV>lC`L}WAv=#`t3kjb8q<6vwInXo2!M&$^joH0@lcjDMy8q19A zeV|n)g$w4Qs+VgB%MGe2ZcUGVPH^BD>t$$sv(6pCaNuN?v!c{8>7}BkC}Xp2K8g zU7dyLMSQCLi&1J{6M5o5gIB0a!3YXYVoVgISQzg}?9s@3FW40pGPegQsqnK;={6 zO`y@<^QAKXz-N z0y}F=#?GYm;a4+q4~lg(@S4SY{Dw1TFiz-5rC7LgK59F+x<1>rLrhk)5)s{d<^>x( zjtS6M_N*uKh|yl(=VB_6~70dTNC{v||O@^1IzYl4Z&oKVrg2#MfL#1@pN>-QPn28kH`rw~UbD$SE zQ-75+3SS+;d%la@|JY$8o_-&8tn}@lPq{XN2$)6s)Nd;0i$}_0Ym%bi(#rSGisD3> zerFO}-fJZ1Zx=1@k9ErZSyGUHjlbn*HP`q;mt;AkJ4*k{q#yMWnFMuMbqtn)AMSNK ziGwGx72VQZrg4U}giDSl53h-dL32~Elu(`NV}B~3QJ9e?eP(MLi&yYg|5IdZ5@y@b zxS4+wa_QC}uv~j(otd9_>@)1JtRDD=-`og7ml4<}J{N#8?YwT$omq{_) zn)G$L_qMAvo)y2E&!41h8WpbQsyF?=XVlDa-W{tKx3?;X49zCuX)$+ zYkc1Fq;G#jM1Pf->=*53_+D|0k*@mHUvL%;yLxmN{qtp;;fJd(_WZ~03oqBxU+D0p znHz`i6}XX)X?|eQp;2gqpcxG&o|{?dP_%IG7Cv+ct}m6=DSXa*r8l!(_OjFusXtM|Ap$}8K1B@1Mcr7 zdPmYQd+8fNfnkiag#SAg4_5G<9sRggiq$G^(!6x<-G$@b5@%2pf%y%Y)Fb!Ly5BgN zGf1sS78+YO*ZE?!)8#-^f)^`IM`; z<-H@39+D1auCI`E&Qxv>|K()ie!LQ}x7EM76>*?~Po!h{hK8#fsTR4T=BWnKue9r} zoIHXwe`{?kjKVZfp#ML*-a8u3uL~R2dx;h;N)Uvo(Yr*6P7o2%g6NDodJxe|q6X1> z?+l|%ND#dfMvpp-ZglVM_v9(>x4w7f4{K$u;huA!v(MhwzOHMpGl}>q|b?jMQ7=7E=4wO{f##VeUYNu@)2=Y;dA;K|w+&w~q@Z3#m zzxe7YZ)jSt{M_L1iTxy$Ge~<_wpcwHUR|&`cYiSMy9&eXIlw#Syjpq7S5V`KgZwFJ z{}UU&wV;4%8rt~wIG(;k(%NK9X6k1dTayVnC1(UodZ638Hhmm;W1ZWpGJmUvGsR{} zK*t~NGV?3k^iv2I$lwdKg0F4%IGk0$tk;ZkhZ_dLW%`jOZ2#<$MpE{`*|eAgH!bZa z)9?)o;bTWE)~$M}0&X#gx=B7NP`AD?ub*meSP}d@!A&hHCBXpu@-SL9ae>A=~$4CNEo5{?|3iChr zRtc(G$#izg0>bub7KZy?Z|{?^`g$Uly%)##U~R5ySFe#8bK8nyw5btVZSjSX(A?CW z394q^ClE^(z5e?H_+t!!vMg91VM|D{4c_~Wc`I@0fP;+C*N2Z4(BSZ!ixH4dN_WQSQ6i*AuU%>3u&PMi(AVOjBp27f(L? zEtA~np&x(I4{P(X{utzYusvK+^eQMG2i}&6WmNm;sr%;VT^c{ZyA7oajfIH(%?dUN zu)HBX=-xmXHfIN8nB&bGEwR=!0H%QP+w^KrRO;g1q7M7N9!(h~he6*=#ipkS&+g6M z7gUOdmZxiv%I*$#4XItCB{)4_-d5C9ODz>9yWC^TU)z=M9`}E4)R$jWL|EMb-a~F; z9MS%hp;Iw$npD2Ub|KB-up}1;4&RuYu^d?P0)A*>zzR23(VTn%Hm|fuYN&9{pN57Y zy2aNM)Fao3=9w-}p_q{W()Xjvzq&VKK zhl-I+PSZMbnz?3+2pvDw%9(?5LjSy353Wl`na>4ehV%zDAE?Zh+CQ9lHgy9_cCs<0 zTPCN1iEQEczL5Hq6mT7V#OuQE9~zHs#Da^7QqWs<`Ew+CD7tnnNAzO$RS#Mm_Ewj*kZ# z#^r)`1h;S6sn#P;rI&!6{xJ-9a}lfj^>6l`S+C<%raxg8$f^_l>5rxat0HnsP~ zu=*Kee04!|crfY^B$~IX5eIhRHi(x75-V;2hligHD<2>k#Lx!QM6H(?t;~hof2R&) z9=v>8D;=B>C2L4$up3HD|6Ej&Kk2HaMEYcqImS) zW+uDcCb(TJjzRR{m-Po5`2-N%f)>6TogLeW*e&LgSu#AfY{mIl}W>F4$Eb@Z+carEv(h48%fS2*1Ga`zB2oTDIGL<ruW~Az zlL`;WUK<^mz_v5UXc5s|){~lDr_Id!YF!zq-T+T@urO_@zR8styO#dxE9oF; zbh7Ah+3OPW&}f?2=cJO+LB5F*lZy3W;<8VO*hds)){d(dMxOo<+A@PwJq&P_&4I#2$wxeLN-xC@LkK%0v}hm1>Y zQy-G1DK&oJs$_#a)NACMCQe}-i`O#w^uTv`i__I0Ij8KzR?c8-0h^^mP23iXc2q&^reK% z)aan+N{UXgLB5v1&anPQLl$jjpE14d5RCa%7Pzt8!cHC9y1>!}z4MQbAt^PfldHva zXB^5DY*eajlb%5(4P|J~{kIp@%K<1@;&Lt<;lJcyU*PriV!OyQU=9MdtS_B5~_ zAY?jb*j$hK^!B;R5R=sxqo#$QpPKFsq{cC4GfbG$0b!IAH|C%PxTaKjcc(jw9dV6C zBX0c3^qeUjTl+4g@s&2P(9b#j&6XGyd3gK())aaKXMjD?=x(hC5CJHcNR}3!#4jj7+IejI+8qSTTEH<4^PA-6OP4*T7WA>EU%Gf=MCWw*w-t36GP`^d1b+Anu7r> zJGU&b>;bS0Git>bEeW1VTL!NorI@K$Dbz#hyM*sa^p<4S&LhN{##2d33NoVoba3f% zm5^Jxo47=@5k*<4Txv zzSk$%HB7zetd3PZEQWY?uef77Qtg4FE^${Y;4q@}9F_mkpu;K?!zarjB6s0K@Dv{= zY6TNX!N?F(RtXW>bUpQxC2-d|P1_pD$(VuZ##K+N*RB#BHE&_Vf3<7_iKx=l%wCJi z@m^H5k0P?%H5mD{96{XnqcB3sy-^S;Xnp~MNk^;xC*BoP;b9;xgP8NiiC2TX)M&X= zGy5+TccDL4E9M%1?p|L+G~q0`kxt##E^w_=EmzL|(l(!bp+vo0$*!wG35oKYd0AMT z!ZbZh@L->Sk{Be)I)NXUpe0$t86dK2uBS%((@BZ)DIvX8h7Q6!5c)gOW^=z#19bb5 zlrD=%7$Fkl^wkZf)e#Vg)s64)i?v)?z`K&%#+^yNIwPA#8b>SaxcoF;M(49i;BR95 zdB;SdrOGJvA{pf^4%-fR7puVunwE%bmEw`bkI>M|O|Mhz9F`|)eFaKd@?Pld&6?BB zLam$h!o_?ru?j=fL;0#K>v|H1u6rx}fDXeS({d=uq-PgK33D0QVZrdP-$AANWj`8J z?B92FYy8B3_e7^N=cZ0RUw^+p9x4*M2~s`^6Bo5%R1*Xhh1+=K@$C3tWuVqAmX^Gt zqT?+e=hdBlMg*@r9K;t)ty35mnVedgAIY6HDDPevKkZyuYyO3gj>>lF8`jwVn5Aem zvQbD4+H?I`;F7K<^s28a8@gBGCYSEkQZz<>eF^Yvb(yQya?e`ve8|-sOVSgn5ifQ{ zF;Tf+6)O||bzLz7J=+M~3OsPCSWte|gUXU2G+s6||Tw-#hRm z447mRY?bBjL=}mPyiw8BudZ~$37=ME^CS9&2Ht)xVbrH!`0{Ns@O$6>eUJ07`Q0_A zg>-C7J|Tuyj6mr2i4(dz{sxbk^TaejJ~#%ZlmS_JrH`?~n-OZS1`Xp7;WjPPmpw87 zcpC7rJ$zxlD(?PgTy*|Y16;w$8*S*gNk&9m3XO^>{u2?q5wt(h^qnsN2bqD!xZ#4{ zT?)|&2>VWY`qv+&_DVC9XE#S~V>0=eDe7rcFTc?E5_YaTtUjeQKK`a4+yO;~1@k~8 zUJ?Cb{8*pI0rX$U_2_SVZAO$LFY(+Qz1Fa^TkzhKO}wNa(lzBwWp;D@Ba#*i*W)O=v0_}HX%>8I$rkA=N3rahYt^2dmnzi=twz08aW$1$6H%woPF7ClMv)9$Q?3lh27VPQEv;L z0LJc>mQ{8C;K+m}^Q5y9#*>qxxOfgM*3(x!?Ku>ZbdcQ{<7D%d0E73~w8%|(beBLI zDBuM4uB|;Ja78`$hG5c%rJ#(@M8yZeR8DFX#!PL2&qta~U>WU1;I`#t?VH&MyJW5< zqOn^_hr(-HhB9Gkxh`kq50) zkAt2E2V(tdC8^>(VJ4)kBL0*uK>k3GMR!$R_Qd4_8o1;W6Z6T(JRv4a=xPCX%ohi1 zc=ebw!&A`WZL(mC?;GIG@4wHEM?~EtoG@^`hY9e z>I6;ctBS}4f9I~z!PxJ(g!h-utuyOFmwnONORx45-Sm zjveUWN9?s<-v>aGZh#zP-oL(C{h7VU@BFZLc(|wC|56+1B!4+R9sFHa?Y1#wnZlF> z^xs}%Z!(Uwc+kT~@^GiF4+-t2N*c3$}Jy;p^hbtt2Q_5!(_qVj&pb#vX_2_uN^7449MqHa3BaX#YBZP ziO(vXEqTB6=3C{~Jw%T?p^?<~EEJSNvL$FpEZ&8*6M|Toi*N|9(m~`&Fkb0kltiW_ z3(CDZGm|Epm_#DjUCol$eS>XWGirTM;rLzH>2I6SD;xBbHnRjMLsfodz?U==M7{p$ z)_uxTx;TT}xsq|~U~=JY624*)P%o$7`C2LLGM)N!Aw?Gks`@Bz4kj>r5cWY&3DAhNH4i?ysrvR~;y&GL^E@E-?ya7GHQJZFrLF#(P-I ztM)Ip>Aa7e_MmLoseNQD`fTh|=T8IS4>gy2fFq}ZXSxW+$P?rCx0J5R5c;ZfOw{qQ z`|-}?U7wnEvD_qPlvSx)@kf*G@0SWge7A>cb=_WzUfbzefG~$oXNgyzvjTPQ)serR{{9Hefb{rm^Cy#c<;|y1 zohhSDXiA|O*W5$q6)NHidu4FOnfDAL5bB$85u@DnYuQ>nzfS!#;$gXyi5U;ke#{}Z z9&S>X(tywR=X{)HysUh9>sHj?VbV2tsA>eyKwDtzmOee($pf%t%lV0sLBMphspPhA z#M`GWF?Me{PDo0)OG)Yus^*vvE_O85({gUS{55eOIq*CNV5kH$A(N`XEu`pT>>@zW zwF{tuq%bm5t;el5am|O=UyZZ>l9GbUx1>3` z%@ZqqU)X-SyCv(xckp6mO|D1Tba7B}#hV3GA(tM8FrX`HK3O_><}t1`%7>BAaec~7Cu|o&K*hC$sA})t z>u}a^2s$z>mlNOwZLJxYDLav@52tff&ehioy$x7PnyFcJZatbx&+*lBhZ$c$xK(ti zNKq7;VkQo+d2d&^t=XWoxs^&k#?92(oj-bGS0-FXbLO=sf5{9&e~y#)TcF2mZQ7KB z6#kss2kpK(B?OUes7LA=uZ#QS%7M|H`KhX9`*QFz8xNn2H9+FD6Ff1&XsQw?SK z$Ky7RZn+|z`f##{gJgnr;QCSDN%HoI+=*gC!WaLM`Mo?iZUQ<|F$Z({*Wa2(gbI%o zuEA!(E}LZcNh3{ltsw4Fg(EB2q-lX`b5LX63AP~ZtKiWx0>BVw$X7ebg$!*gsHs)EyW~2oW#*jto`Ott=?`N$8BTadn;-^+hw(D6g3op z@sa`P^cEYIiMYBoTI_6&&MLmphVC(3@6T0ruq5B72(;h#N16A;GCCRks z@L4quXg$QdtuJf z>+0?>i{oB0vF)!Gpz9dXcjp`Z`!`qK80(8OZMaA#af~&=r=wLLK3uhXl!be8h{-w) z(2!kAI!s)@i2C_M>3PMhhjz^~>}W<|a;WDnS2{IaT+0AgHGmR8B!TunCs^pb9!g%K zn$E9d@@bpA1sd2|Zp7A;yheyVYxzLPfs>9YKuqRhlNHE3%lC_!bs7tz=%Nlk=}l2g zZY!X$7x>1+XdU4j7v9V4#_)s&QLhysrh)*ImZlmF%9aq$QvH$D}&%B{`) zXpT+AGeZfzKcW(M-YWrH!~B@kqK@2(mj;3vvCwmk+z61p1e?k9F=4+6X> zh?s;`M>4-W4+CZ&w}Vq>i#Gv!u>kWcW~f<*Q*_V{8Icg*ZQQIXB}DlI#Idx1n7Up0 zeyNOqroJI+JEnIgt()(g-nct*-g*SO4PS?FkX5hByHzZhLD7+%Yof!9s5v;K&a`ov!eyl7c(LBes`K-oi2}6@A`?-%QKB z%_G(pE+f!He&(q(n$N*R{`45!tmLardvo~9`io4rZTaz!cpoo+oK2agTqmE+3uC%K zC{m@G|Gv8kOloIQ41ha$ta%6iMjI>wuTIa8ri2g#vjTYw;pw|zkEg+7FahN4Y)0d3 zB>H5Te$QqYqzl0WKw34 zmf6c6IOEI9H5N6#DA~?{V5#gmD?q7F`MXb>?RN80IcI~X36J!+LH3GyTk7O^{EPoe zgx8;(Fwk#(gB&O0u4f=ba>Z8xf4U1&n!ssOUt-e_k9a(zE5ErRy~V`13CB$Y4wv;R z-_m{zi@Ud!ncX_qm9b-eKA)wayCs?(- z`&nys=2vncx}6h4zj~+Sy6y^Fj)fw-jhtJ9*7{|r_vR(N0BWoRmF!mw-r%(X{aTF2 z`q}nPh#Jq`H-5F2HI9$nekRQvfK_S|9}BSztx3-Stmg<->d;rOuzbCiG)>;U%5N=k z?Fxcnpn0T8${SdT+P-`;u& zIgtkQ+Hf1fdL%8=_^F2W{>jK-%24g&ox%d>HK*Hp(y{3FSNF9xmg;^k5X$F}V@H>$ zrJ11CG2y5aCQZeihWJYfdhrUq0}qT|Z&lq)l#`FcGs9~nDjo7M8|rP6)Te)k2-wV2 zKTLjN5?#b0^C{RLJ2P1@R-ybItDJl1xUdXWvS4jY{qb=~pzp4P2Y}UB@Y}8^m@huN zL0Efihu|v3-qZFO4P#o00)|BgQl1CE4>-a%&!VG=evFO%Ku`vyWu=7q;*$JmZ9b|V zz?%Eh1>|L!jsTy~Bu&aNvEpR`LFnxV?v&-{VcAxQDHm7<+GWR(4DPUoPv18&A4@bA ziOd}kXUlaIRj90NkmsRm(Sffb#MI+t^6X}AuX;0&^ryH+n#j)>X`go!)j5#5Tj^MZ z3Lk8PNNP*+o`wkh3qm7}TR0ba39~WhW=3wHQA*S}v@f_4r{kY|A6Kg^u zK%z)$z<#qna`bgyuPgF!1c6DN;ZIVZTQnt{cw^x*b=eLgz8i(P}qv{K4x zcOJLmO;cx+{nXNE7~}ng1(7)e={XNFWMh=1hy0tq8R=?szZEiM@T-SBtU!K_LrkB0 z>yOFrx_OV1^8QL~f0f)_qjkUC*X4rtmi(6<^+0-5Bo+4xU{KeY_2DW=AdhK86J=Wj zR)C)F9(>?y<77U2lwus)yf^qAIS(COlfC_Zr+)!_$(8|9rK0C031klmt!G3o`lBk) z4u@rrgc==wV?=5@j@3t?t!7AMMdiiWzW;t{U)8}_XFc=-(9De7wqI8NS^fFC_RM0U zM7O6l@!{|ph2*!8=|ZjN3kz+KmF=>F>8!c^!`r=$CG##?2=&^}eh=zq4EI}mV6gg) z|FYstgByq4WMxcE9@D*P&BVN!ASlMlPadPQuCJ&35k3%1mh;2F?;n5q+YDgl(L$6B zDUr30&>_k-PdRO2(!JA@grCD=65WPVs1;tkCmv}IxIQ?=+Y2YA4}SRJYx`EKr!Fzv zWAs|yKkuMT@Y>_8$J$Su8CbImaDZP(Uta=!e_2owLYTWu^%LpE9lL9=_Vjm~kvR0a!h z9*N|qQooMkK*^wzVt+3tIEX&zUpij1i5FpOOaFd5pA6fPBs$B*gjb zI&oa9CxWAqnsF=9{Wv@?_6I;qn9u|_yRL=!6P@SjJ{zy|0dc56DDQ1Ej`L9FBL@(0fp~cNU0JYP+?2aXECWyV++6;l zh$B24h<6cys#0t=UtY)6K$Yflb+$pVA-~z=74sUwNLyq)<#^$Yu9g%dZKG~jBnc#4 zW~Ux~TQ!<1cRs4K%NQ7%vo@jYVxJ|``|H>DY~Q_qh?Cs-FlkjLReDfS^FgGYL&iKA zf)&mFkk+)_J!ij7>x7K5x#(y|t6(yGsE=(_13yDV{^k;3Lw@gbo(d-piJ=!YU)vUb z-4T#rbxvUw^YZdnY3wcTzj&5%f&^!Czh=zloVpI(1We+u<^eW3vH#$T{=uz4z0*97|KoH<%%XPJ(9)?2@b|mjZR$i^ z-^pKRJxIRib7bvj3`Hh!;m2&+qMb0~ndUsh{!-qMEe8?0WW<^vmDprP@80^bEl-ba zX?rwWY5eNLmOsX!UQ=a&i{_Ki4>#vME}Gi1Xa29UpKzLCqWtlVnHG`-(P75$+>8iK z;Hgq>q3<3)M|m`vLX(6hTldEIHcY+HE1QuLSLYj9o8UP|;D#_9(2>t|;Evq$(Z0<+ z`_4fq`0-)|6_9H-K8LgZRJ;D|3@aX8SVd3SH`70vB$ST`irse+4xc(N#=>ZucxMG7 zQvxOsY`;C|upvc)==n~Wy3IAu+K}`H!m-Gx_EL^mfCU0< z;%al>_$xNQCy$|wD=m1_{saS+>q8#xPHchS1Cvgr>ffmSm)1M~S5(?i@;(6$)QrBe;O-6J0u9| zlo@L`MI?%9QB){R>3@QIas8`@!)zC|SoH2q^Bw5SDNjHN@F&vW2JPE%fGQOrWnYk$ zcwnmh1gVv`6unW41<$`SfttuGF^sGBLWef8_%e%|>-HG#*3RvpwIi_HM`Te)pJFim z3Ldbm2MQ0}bQ2wC|5<|+6(E^{V7B!iwBfu$VstP&&ak9jC)-EKD6J_D;lvwn^eaf)JFJ8Gw#}E@~ zq7^(kPNpNO`$EfjC?R{$36ZnZqn`jMUJvyYFsb#ePRMyq7z4Y5dBFMk z2IR$2@rjG~-2$iHEwJd$J0QXWLJ_6PgqL>l`Ug%6f*?L=8WuM!y1DG*x^{=E6t8V-Xn63}**X z@S|WF_aRzqW13%dJ<_->HVPPymdy!~&qb&B8`13MB#K|Xl!BLJ>11c66q@LCSW!WI z<$rUkFnMS>@q?zTV3s)@(-$YR%%g)EGnX|Ff~IwqO{SxrSb6Nyb@n2xh#|g%DrGz4 z&mG~AlgWzyY;lVvMvo-dyKi`>^wI;QA9JhmB@#SVmScL6@j}cHDR>8cl_cvpfGxIC z@EA38Mq2d)|cKfqi_O;}++I3nPSuZfECW7p5!pI;njlY}O`k|zt%nexlk z>8d5>`)m*&2z44~i9QJ`%}XJEZitP{*AS$qH9@_=L0*&@;EHs?>8%`1P~?0|NDtYl zu|k7)npO7Q_N#)b=|U#6fscym+3ld^=qLrW=EaSO>6XJfrguw>YZPhy|31xcf#5&-p@J(xNJ4Wyk*6yEGv0aP>fN+}MVCCv?v%b!z(UT2EAN$&yR!l=_L2azj~ zr(_GAR+%`@yXla1ZVZ%0euQaF(>q5{ZXvM+b7x+J7+y*&G5+lSPNj zT8&7f%n|E@@71{Ki?Sxe!8!?(x!0BTP?z?U;sT zAEDrRP;|6QnGWNOn?t+?^Lcz zp9bKiX8YAS#cnT*rEYiXy!JB0iQsu!75++suzBw7izM5pqnD>Q?ei@_w_k1q=Kq;V zEQqBlPY4rCmq49=5Vgrm5H9Dh!sa?mxbxBSzkYqnk*f%CDxcmjTVLxSvhX6RU zM+5%ziC9aOb29`NQlD474$dPoQ53Sn{#8Y>S4b=OTRL&~z^OT-Qw}m3`gOPQy{A7A z0eatH#;A}NGa4{{lqWbAa-e$Blm9LnKfM!)#v!mD#;6C-}js-#u-WhH41*t^_76|lz?4HR3z(IH$ zeQj;+kN$cx`KmJI5g)v|sR_Xr4w5V8dLO4m%i5Lz#kLPvj{H0~f1b8VT4&mnns?!B z`VFPVo@jjO>1niC-&EBBV!kidAQX;PmZpy#mGWeA!9o=SgnH9ae^aRO!I<7f^XU10 zIx}Q?85e$?Ih%8V*E+s2b`hgAHf6EJSXi&PS7CDP_+W>$<$bHjVX)kX;q;auk+S)) ztRgQLfdAD&+F99Oj7ru!J4DPLW1O30p#!^i=s^mgVY*XBaS~u-zwc-cTE=o%Hw2Z*3?MBM3Ry zJl=wz^X;-8*qs-{p4`E}>XvXa4^R_k#8lSE62o~@ z8(UcnOEm3LM8eq|Je>(f5Y6I}D=f)sbfBUduiuL=b~k+nehG-k!>3uc)_`Sni`?;ordMr=I%+7Q831cDvfWBXMRfSJ2+l-fTPEXUe1PY;b&?ng|B;8# zt27q9DjZ~E@IqlCm5gt)%mduivkcukUi?Wr2p-+uC>)4#aX5OPnU$eexN1o4#g4_J z3549z`81`I>ghoh9e_aPsMwHXzQlN<8`S>~p&u}8O2SwenfFu10L!IAbtxqC2ZeZ z^U2GG;`KNLpp6TPFF>0Y?S*FC3~G%)jbmTfKYos{15}(8KgJ!osbCq%fW)1aTYuEM zjUi(i37dNWMJN-%#?<%?molGJfA-&-M=`bv`YSDI1Naz{O24E|`~bt&ye;M<)=A_m zQcO9#Lb!*r^YCDDcH<-}ilx@LLY6arva0GhKUC5+wJ$|sNpqF$^9J=n8GV^wOGvIY z1%QpLx6}g{?o{lA?reYY#a$+QaV?T$d1>O9Lb=4~xe9ArU~?}VQ~~%QaEfap`ln79 z-Ldq1B8(J3_TBbs@?aFQC6h1Hyu$baI0M^Ji`~IH_Mdn4zg|X7>oNw2n|Drd_j^4m zEaVu30}No!K3BtkN4u9WzE0zrlGhG~ou1nf<6RjhIX_AQV8-99W?PFL8}~qfgmU}S zJQQqk9R3BPRZ`>KTXw<1wiunTt13MXnItRIK!ffHp*3$ zT)75I-MIWo*>1@!q-XuD-G%o7^mT$Es|j7phM0|vXY?SjQT!hILEs`g~qw4eL&0j4i9T;k`vtGPYqmp zZk65#;*)9H|2rk{-wqC%`sicg}63V=)7;qyT)O{$aEeENPyMP^$-% zwDbTPEM>FEc*hHL%#$ljbjY|@E6Xi>NxU!RN?}TVqE{MBA29?i56dlmOfcgpo!I+2 zcu0jUUjLaegAi8)%-HT8PJ3T1SG^h~BaUrVyXkBow6)`0`d418Rzr6rfn>s@tj$6j zxe)N;H6`L32F{d~dkpElD+D~Qw`G_*(ZGh_uNn#$<#b{bjcRJ1L*)K%!jY+o+@IDb z_QG3mJUq5!!aCPyvoDcR!y3o)SVZ=Z*F-hv6U=_G-#3^$nk5?ZC*cRh9Y(;cfT(qs z_C~4(?ZYwxouv#Z_`Wn%;Utv#$9_Cu_j4Y}CPUH--Am#&=pNUSiFm}sM}mb!{wc%p zYVytQJkvu1TZD)hP1e|nzY^UbPHQS&`?x*cR%QQur{Ij3?Mo=RSkFA^Ioc#Yuh7*69Zi&NWW#N-I!iQ0a1pvL*ko2<4P5(S+$I$OR1y|z)J~qgMHi0 z)Nvam)3gxje||{=;dr`Sf`P0hJuXPh&D=8W_-OVWx;NM}Vf&OpW>MZkTsu%QMt4J0 ztqLr4iD8eK%eiZrKP54vbWe@Cc|%y1ycKM-$jiTyvCYm0xTIvXn#w_;!&s2Y)|GdQ z0WG2!cj$;fSo8h)5ne+t8u#n5%K|b;zso4aL%{K|)L62{TS-qKciE1_fD&WO+PwIY;!9Kf3_viky+LF6Q^-M^$xuJ$8xEkGC7aSk z4u01gNPWQfDBg2J8)T|GAu}(znEF(9V6AW-9zy^DdbG6(0qL}5uReSP*qAhSQZ3I+ z_@#CV<-Ufti4=KgSqp$j^Egw?|K5PA*cjJyfCGs9v+E$qByEHqrlaGnr*-PUu{sg0 zI7SSeaA(*a-w(oJnX;xtfs)kkSGzVotV70(>{A>cE$Y-u0rmqU+i^<7JuAvG$+Su1 zq3se3!59vF1$M)J)`dPb``DCv(MTGL3B{r9+2pkeH+OqgjZGUlXDDBH>ul9;dPXJL z2?5#&s>h|%5^A&wZI0#$_AsX`j-JPe(eGfsjNO{w%X0^gztf3d#g#w)d|iE;h`ge-@mDJgG5u2Y#-*1%ANm~3*OD6cpf`40CN5A;~n7}pykjxXu zIBJ0p>Y_nlc`tDXx+)S#SOlo!dMKb*=0L%(?t-3(Uh2n-5aT+h@%VZjGW`wATB%3o z`IbRgJhio@;R5UH$a~D&tB=%b%)7cVUExxkn8{zVc)d?WZL}8!P~7-$9!E$<>GA~& zap~m=H@mDBVmkSc`8}Asq7iZqsDC6HbF>TIP@di48fxaUbxg1#MHhRsDlRT5hiJ*4 zAD=*cKIT@LHkmokAO4acX&Kd^=0kCnJZ-CD*a38cOBvlwU+!fb)+LBgand%Vg-evj zJ}UWkIcwPAVi9q!&Z#2dV)|G9@j3_~>S4nf)cY8Ec%QRm9IVx9xxkEbUM07wn-ht; zciFJkXq>;n{P{D3xCckhFb}8OjK-uA`)coMT%IJJ` z^bD|9DFty+dEHyG|Neg>J~msT$jSqKDJP=ENXy2~`@6;FTuy*)ES}^!^`(AMbT0A1 zt*W|@IT1&e50}&SDCX_X?57AVsh}iNf5(hs7LU>R--V;1q)Y8gEb#sx7|ZfNF)LTY zy+jIMm7eC=M@E90q~Nv+l=GDr&~)D-mnot3O&(G(8Wp$R56D5>Kg#`^KJKpvrC%%n znqmzf74p9>hCWo;y{Dn21F+6cE`{aLnCn-7hk9}Snj6dnNe#sJfyPq2}iq+s7UnCd#0kCU8#ENDac z4&o^=L>C3}H%iD8%gqM7BKZ6U~x(tj6VV66mQe$j8>0vg)rO#b_1 zMtzCv*xmkr(_bLm>!PcUskUEX!94=t0sc|(FY3g4dL92}U4S-E?zfIyrzg*A151!X za~W*D`hjS4FoR}qVO)!nDljqG&SIoG$7U=aKj6(JYbY_TUCs^Qu-i;_Z)0sJSmRGiL=kp7TTb5=NE5NoUyIf~>#;2{;) zyFbm(LV0~7Q>shhWpVs z{oENqU7zNs-+U5f(z}Cm{d+`Q3*L4t4$jlvL&}h{nv0lIcNg-r)Y^|Rd_D;Y{)C}OWQcZA^A=s;j2L^sf zW8!3Qk!c)U0zLTC6}8tlYX6I1ZyZp zH+G98U}nOkBDHCB6d#oG(H-(@hk?krrgvM!b(oS!7?CW80yhi=`4Jrz zx~r`4lCds2QKhW*rW~c1w6E6}Pg-RxLT!mwqb&_S)Dc_1h0RBUoK;}{8xDVvfCo+d z)C1Hf;16^@)_$6Yu35ey!wA7O?87T}x~w+}*u9?(IBQk_V-Cb7t;AW)TD=B~5t-eg6b;}+#?pX=L+4|YL|0B2$dE+vsCU0W834{#okayg1E(ZKs|Rm4Mt=d#GeOE! z^aitdAWpuW^e|)g#c=cE=N6;Iqd8uviY9?`?5AI!0NjO3Lj$MS|5@v53`0Xhu|~zG z%X=mbc2uL|-@Y-tZuTJ-ahQ1>>^o!(z^eaB?_USiRst6@b>O^qk70N2+PbgaklehI zA?m~0-Y3sP{jf(w=)OhR8gn}i@L6b6@zITWSIucy!vSnQWUC!!tde%Ig#;gn0{6(t z+R&;swrT-}@E9UT;}t!3X9kY^+-DpI4tts-#&UM$Bw22tyDkc?5x12;$4eH()vt`k zWulAP7PedY`77bzG~L-Kt+qhDyg2Na9O}odXT>UE|W{x7qwh(;FrGCWM+pOMYQNstjCflGkzA@sjG%O-4dt zp=$}q_)e#*P7t17@(*=Ioivh5%&w3A);c!|MbZW_#B@iBlCY# zyZ`(7upZw7X7wc$zR5S5DwgwjWLp~4pCLCd^PGKNSM4_=vb)T4t5WoB+_+?BkmCoZ zE{K3d_4)LoLeSUV9J?lRQh3H*!wMcM+X}V^L@zn~SIqh_VF>4PLi_~WQ$viYNUNkpntEA7x8xtRY=MIN<16S@}JpTXwaj9{~KjOHbqVN-E ziQ@&KKj|6SIGu9=WC>^IMW+0Iz4vxzPe=h_ zRJu=P>VFUZ|DF`UawQ#OG!TkEk1YyTg6M8GS5w`?z`_JK7}G=OD-Z3y`3{@3fdKos z8Ni3GJBBt2;Ix^s*`CCJz_Wv(5MNuPB)BPX4z;PmiQtdI!H+bAnW47Jnzs^x(5*$_ zT~9))NEyU2?i`5QFAlk*8Bfp%)XVtp($T*%$N#Z?`}cAMzPo*AFCFTcjcg@N#w(X^)4<29D*{5_ty2Xqb z$xr7z1VI}GcV1fshC#hBV~y-m1;}{YbZ?0HYmnrb&im z|13(|dX~Wj@`dE_hXNq6`guo+q9k8CKdp1eNh}WuAbT|Bgk%1{A|Iy=Hqp`jk0WP6 z0M-!|%wC}bjI>zfHIJE7?~av<6HO!YFd{>S-gk1>^e@xS!El0Jdp__!9%X6q@63x^ z25g?2`h0-&+QlWr_n_jb)7`d-H(+)Jj`)(3GDNpi?;YaUkU61{yjWB-78jXlkcZCb zvqL73{iQ3^Vr{x?=R4JXmRG=Zm4r(US)0nB|JSwy+`tOu^6|j-AfO7Q`U;hXI|e#& zvp$4k9#PLqA4KXq!_Pop%K5m<2m&s)IHZJ^@f~gvdA+O= zz)RX&2@279IK|xUY%FxIm9Tlv4G>}sKKhmS{y&%Q|6CAv`H?L4Go!w6Rd>Mn?|sTE z3=`PAxhRFMcFT)2E}b^xsOz)?6M#+xIt5Hyf}?nq-TOmb{wP1a!bijeP)FUIe_WOh}DV8TJ-F1W6e z{bObI|8q#?atU5Afp0m%YsOp4yk|{hIx5R+$F*3mDN;o`BtR%4EfAF6 z-sbnl9q-=z@djrMGV(`qvd`YD%r)0siC!<$h^((}o@0O#n8sY=k}-nn;D%>ct%LAf z>)jZvbbaOV{cd-ErtJ)tcqiJMUAz4`PdE3XDMr_f{)-DhF}*GJ=t;wEZa&CcnRxL& zxCJdxgwyB%tleeS;JHE9>4cm#(`Sd>%mASZSv-d%M#G9j+8~c$9qpLkIhr4xT7-h6 z);{I*+NWy7TQ~nWU3|CIu}$l{|H-3!Iunph8j6?B_51!?F#7-OmS~@K&WQ<1-EWE6cMaBT7{1`hOp| zU{-&y7ZnvP#K~=btG4g0gnoVfV>w)KVjZ+nbl-`50{H!r3TnyDK`x|L&>=NnpP$3~ zfompshWq;_WtMrve#_7Pb{jmPQv?s}qt}0;2|=iEe!t;Dr$FwsU_8t*EC49U;@c)f zJ|`K|4qN+Ot!R}Th88A*LdMVLZp7aI@AoG#Tz&0g&-C!IE+@xnBG5D}4fqflAP-dh zGmI3N&gFD<-Xg|DDom%j>(d7mC`-V zeAgpddf^)cKAU7*xr_+bnSA2Y<3IZ1&nGCZ9_>B_I`nM9!sN7kM#=y`t+egBDGo%i zgOBmoB};9Dr=$@U5=L+%;#RjQyAx|6F={y&?psG1gl{!N;pUO_3Yq zys6-PS^~;>G7z*$ki5IA_pLCN(IC&^22=0@?Ixw*79Jn9G$HoYlMSg^5(#yoa|M1b z({m^JR*z0n#O_AkM4vP}T5!%y9hvE{7$D-_Dm{;kyM(dTF0 zesKb27QMGOi#LI=(r1I_YXj12I$vDJZ&f%-qAUZdANg+5vsYt0@(Vb8I+~R;2fCSH zg3p3oi3_S35#&s=gmN!E@q4p!2CYE~E>mBK2Nh1p7?}Ne%3FRZO|%5c?b6bGw8k{< zZ?%)-zrNn{=y_3;yF#gPs-^@iu+)D^QS%={ z65)`xFyyT@y_Ck}UlGvpG}pj0JxSdZ6vXDIaRjpDa>A0g;wBs$aIT_R5S*R?1T()SIiAC7eUkYS%6^ZoW|;e{y?&|vlls|5K+gVytand&%s97Ep^G{CY%cWu@oI_T ze>j2TkYI(vrb9S7|5NJ zUMw6*^>@A#4hs(`0`ewVFih<5+qb{*uV4phQWkyMEcEn!P^EFw&@PV4RBZpcc-|9M zm%o(aA1W|5;KownWY5O4zGfe3rn`h8Q|K!SKZYYS##VaW+P-Jz5LgE@&@-kXIPBT; z=KKzIC!b3!#yWa3^K!h5QS#jbyB9$T{91c-d*s3rWjiIkIa&RT593|E_iQCS?a?&% zXHQS?2Y*+p59KMaLZTCLKC5Bct2XTX%i|T0NI}cGIn@WRo$+tr0e23$n?)&)c5!Mc zaAObG$eZsE$1GnWOUhdq47jxCdIwv1-bcsPuwSMkdat}oi+hk%Qt~IA6jt&APJjQ( zT*!4+6zK4ZK6haN&Jw-ZyC3^8!=>hhQ`;u56z9Jd0W6DjJh2@gx{Qb3GzIw?nns@Y z8WKfcldDj(>f@}WlHBVk$|O;-_vNS4v81kdWdD0zW3dF2I`(;1`j9Ups&h5i?up&Z z=UgQELqv0}@mEZ}?svo9QF3l$uDtTq=xGvn;#7RK8F*alC#I^_*lif>w@Ac`c3(r& zSLnU}CqJKaa$+Z(F7nA#R}NdDmCOk8H+z$eeUv<$VxAp_L82f{cXvx3Rk8D#z~3;s zw}5I_If`z$=%{aZS8h{T)s9o~WQ*g#V(1OgH&!Jf&&8b0QR?={CZ3Yn1MuT`(1g`A zxW*T?<|%I7+imRjBOTUiKg>{=&q;LSZQ0F79}V8!HEZ|g6DwX|7q~S$OJCMYyedRN9ANgLkPCZuyq`>4gQ< zXDi%WNinTl&QYjdJw9N;44q6a=ex8n)lHwbtb|2IVzZALpLOlJ8X{~wmQtRNHOUk< z?J_)g9Xhnq$+*>Oa~cUm^5-&cGCu+%P?|*Sqx8jf%zWR|Y>NyHnIV0$`cf*an`MPw zOi-k(}(cHvo-)^X409E;xsn z&j%LmAJ?pCYQMvZ`|+bKCEf2{hqSyLH4hy3{eZ@DNxh^dMBF_8ds%<2I~FZ|*;>Q1 z{uX%U%iND)V3T^eu$hCd2oXCtw&R^HXs;7*SgNsUYhvv7|XD<&% zxl=Z+Te3;WXjFQ#qmhdHm-KQJV-w~m(uYnDV(86|*B+SHp1SP0eYS4Cc9T^xOyxQ` zcI#89A)@Qsx4DLF1?c*3k*1?v%jeVO%oY$I`kO4$JSB5Vr*kUD4eFj-S^lBwH-CK&zf&*vPhS|4V%EX9QSmj$WJ)S= z=?lIeUmljyESH>`*_OP52nzGBDmWy^uuM|o9CiY%e6N@4q=$z6&1r&=qZUb5o^A5SrSjwP`|G@-r`-rh#Nb3l)y2} zl(r>aX?3Ox0ns~sxei7Yu}P<^XMbCkEfB4{tE%~&*ICRx)!o@(cY#^kB(|FsAClY$&AZuvy+JN3n8 z5_}#%I)%$rW<%6OJz0d0GCKer3iK;e2CrTHe?cAy@Hcb07su zcf&|6rms8;x$ZRlC+ISs5ZIIf*^qU9wDEBoTnm=p05_}8Z-nK?B(KaQijKKz3*Vmw zXW3&J0_#PTMm8$`zH(}I`X^|VR<^l{je z1-hvXOQ+!2quPq0=X&ei7a6O>B&bkwrI@;n3RGR0rAX!MrGuNd2O!NHG06>=Oz-Na|8K*1ZYSG> zL)OEPKc~l3yfssdH+TQ24;$@#FgU?^t7c(wQ=Jqz2dbt3fxH*+R5_nvqmv8{bpMld zPh;%Bk{SW0GJ#{LayAi6aJp=*s4+4lFg(Dn>Y49h!}3t20f)fhj;T(z_@wulgnnjW z_6=XAcNg6PSYNIXWp;b7b|d?VT5CKjudTLknvbdDqPR|Nu%U2wG6T0KCh#=G4pDOK zBauAdKHhVJR_poDyBFI9yea}pM97J@_lhybx^?fIJB%H)&G1N63V&Weg*A_9xx9l; zPF#g!kQz1!E1;0gd(j?N)+5?4A>Bi^c0|A@tZF}%@i3WGoH3X^%*6-if)lg-&(G91Vk_%Yq>S9Z127vSHvXtAf9% zK#uJZ(-2eF?{02O;<`IUE6wco^0K3y$QSTC=nYFkCq@%QPQ7*-w>kkGVMr4VZYA|m z+QEk5kLVsB?^u&WQ8#Rm_VNH?Y0T1NtL^^Z)tM{bzw_2jjH?uLU(%l>sol`oBc0E} zK1}*oh@!1_wd)8?0`o zKP+9v{?d``p<247gD;3V*+QD`e4L7fo8$$tI)o!DCwvBtI7HPIUDdl0UDbI{{)&jw z;@tc;$Mzf$F}&;jcZ&~o&`-rmp@W&VCc`O_tN2U2CDH;}(MGS{-3V=Fp%k*V#dQ2W|?|m!;4} zE634Q87{|R|MGO;CHaL*^(R&N*G~-mR+{3pA-_p@W5iPu*yF^K%^jr_O(|GyN<%zU zYVU7x{2ocULEh(J*+kJ9RG`|n3uiv7g8Qse_HAGr541n(K(C4mj?qk`9Aq9~w9Ap~ z_I=|qBrsETq;XGnBBhn%kIyUH3 zNI4t$ynk;h=BgcN}letdL%XG_Q`-$+=o?u+d|8%RG3|r2FiPf5Gmm=1eGnj+aT4S~Kw7?Mzrs zH$w2^-5ky>C9^Syb@1;*4G;UHU1{Gf3V+yU+~OjQX@ndxImo9!b2>o!hP7t0g)5TAMdDof&Jr5 z;2@Zk)K2NwC|mb_F&JAOpq>)oK##*6`{dcfX&2p0OOL)o2elB+--R>t8Z+P)D2%2b zo#nT1n@HO4N+RtF8YQ9s3GrwM%pDMxDI4<^u2P2mob8Yg@$t}c+U>?h%v8NmvEl90 z?dLS{Q79>!K)~cFoJ2vmW2#WiA4@3)LN7dZ$`sv-dg}U*rQ~Y+bcDb`NWx~@ha;P= zKXIsct_8bh^Pd!6PmqgzgD-G`hx1bFqs}9>Z?w@nAgrO~wOy)lT9Dl<5 zqnQsQ_8m$${g%Hv?qiiaQV?wkCM|O31YJ90k$G*}8QWebXc@?d24i~5B8yzYr^?}b ze~w(SlZpR6QRiklm>@*8_p>U!>rAZA=Q;?bandUG7H*Z^Gy?gm=h2E!e$13%{5(s) zWzUhMF0I(36nx`6B#@OZNDyqseXtox1Vh^R(4(~XevrYq(B9$5M!k~4V)*vS0CH2k zOHb;_blt{nv6q~2U0ukl&58F4uRV(h_yl?KLHeRqQl6pg7B%VYhMLN%!!&n(&ZnRI z(%-J$nlS}K5c(~j3}nHp^*s8c%-4$1n)JNef><}>fv#{d!@HX%WEvov7EZ}^2J8}=2Cl+X1@f1YVQY*2aH z)p(7IHufV5VmnKW5VtF_i}`1vQmtPojNs&k0)`fMSzOoK3DfWItv%*Q$IIbP!!udI zhYpA4^powqMqHBN22Ra|*iIPjU8-}77B{wJ+52*5wDmu^UiVO*L8FfOtKF5NeNeR* zC~{N%fd~U$L7x|N8K9c_Hoe=bqfq6rBf1Ay=jqxd&Z3^GqMG57sF2LV8r z;@Yfj6rBxZw-wxk={wcQr)5-trR1=F-u9YTKJ4D zqf~0ga>?2Lus`!76hz_J5fO0jboA|;xVW!BJGAjr`KB8iBZ~euYb6S|2jmSS&hDfj z!`0mueG1Rm-&4P~a+mqEz9&vfM2ys=|Ov>&ee# z-Yvqb*mv=YsJ+=h;mdY8*zD^lg);-sYZu1CoKOc~1Kj8yXqXhP@en`?-IiJq{Adgc zle(RnZHmnB+bo3cxn?+xe3eVz^cOfPp;YBqurtzYqBxj8ONQpeg1p`FT6DlU0IaM? zLB!JzA%Z1-cZ0yaQ;;Bo#BdtBId}A%nVRA)7ADO8{>G335%%A+Nb=LuWmav3B}5*C zJ$Yt|?(Fv4A%f%&FS06N-)XT;lfmT3F#?WzaLvJ76lBGlqa8DlED|RW)&ePGc|L69 z$r`T8+#zQefl|QZPu|5!)AI`}GD+zdU=qI1omnD4cEE&P@gpO`#7 zqV^9n0{<%$`sC~~S!!m@Md^$YWF9%aHW+mfMX zuL>QBVS-=ON@McsYvPlu_G*%SKhom{woFROA|()vIVOLvYNc8i24 z^Pvl*>j}oPR()W@MjD7UB{M3Nf@Z|`H<5^^la9*4Bkn&g0W`9+g!(RN)cTSdjR={_rAts*;?Kr6c}$t^_rmTxU!7-q2)mOY z>^yueh0oNd?4L6r!+CRkmh}gm#~_!GLDDU3sWui@Npavr5`+K-^Ef;i5!EyrbQ^%(eV{fn~~zL zHCjdJTk>bjb?W0o7wT8rLtWBA>$iK71*3z>S=G};?V)EX<9MA3Hs!FhU_3 z0uWI7y`Vl6JJ)*7W@)B> zD{;b(D8wcx21$yVtV0J2yf|NHw_dyd^bS$>7Af@DOL^oA6b#a)4N8|9tkr-1oTt1Q zC%cZWx}rP(tje=X&t$*>m z{)-D>6Bl~@W)D7BE4gh6ux;lJI+k{wgU+rzeE5)}>lXgz4F-vCUr(+-%C&nK7DlLz z6|yb~7*aEYvMPF+wcKJ_OsuFyd?{$6XXD$-tal0FIAgi*V#Rb0@t*bP#B*rGbuX9{ zThNvXBXxR^wAyL8*3HrPjsTGlk$i(#J?&?Qhx-bTi>_8@erQ+Ku@U=E?{xluK7M5T zyE;zyc=i?b8OtdhB_-u&z~(yLKY_{20Pp(ge&{t{*s5IVOY-MS9voDU2&?;`QsDu5 z?DnrRa{Qr3S+IGatDlfFd;jG6+ys81y!&?1cX;F6nl!!NLx+5_H2#Y=_9bFzLV~E2 z#7xU5HI1IzqZ{+a!3+CDu$8VX!P-ix>Fm{XdP{RkF})L{+ZR4ncOxSjr;*QZ6|&%S z6&d$6k?wp6A6A2+LqjP>3b^E%8Q2kn<6Fl8q93_Fh96DUI-kp9HpIVCqug%PnH0S( z;UkbL2#IQi;ZT74-j>>WiaG^yzSTw#K@FQI(EqP0U{P&-6C3P~`coF{nur&F`stJJ zpPli7_qfBkD_^mk!Y(eVvVJ`>pMDZrf z`r#Lta7{K-!s=L{w-x)Y8OO-~4irtqS`5yF{>*uE#_TkEGmX{>XOhAA$JEI28Ozj9m3gPkRC?mj0s_rgE`PTlu8ATDky@OEp&(#M9J9NxD>kP@G6Z&8Ar zoGnUSb*=%2*&3x2GicDF9rhL4{S>a@7Dy){ko{_7g834f<`2kWv$x+n$W5}(wnc{BarfiU7;DZPT1XnEPLZRp5Q^v!8YNC z(|8knf48sm<)3Hu#G2zZR9s^QL@fT)@&R!izS{(IMo;>+y~B@koX4IF<%Yj>nm2pE zm&OMn0ZSOp%@U-lINP927j_&x(p$tDEKq{eh+&ES6ng!Rs3;X|##_poUP{lTll1=S z#5?^b?T0r$Ta>|Fdi=NM?#M()TR%7BIrQ2L882#yr90eh$F@fpSo>bdy#;O42iyAbDJUEn zqy!HSf99^mXBgAt^M`u4#+|kA16~H4#<=;8=)B=rDXm-cLv?`xs%g<*WAuw4zO16z zBE%mqUv8zv$>u3!YSQNv7W*W}9IBJ-p%7vtDfP0$uzK7KC_t~LnxZI-qSPjw`WG93!W1}|?<1MH$k~X>4)cT*V zVqp^yCu2T6vN^6_*de+}l9ZB_DdpNZSmS7WvfB!sPn6~yk5})6IVUIvFhlw1vt^ek zyiBz}`isBrIwl(|Y|F@sIQ7Xp zpG9Vdm>u3xl=;=g`iiGjGA_0w5Q22zMb}vFd9RIiG?Sy_>t}tbk+6+wDR+;Rg6F|I z#=hRigdgPTCv0xn!30|L2hbIJK@!tHZ zb45oYhX#KAo&F^KhL0W+V2tR2WcBPsQM#U}MZXI%Zo7xdYKYG~bZ6w4( zM5Lr$%fm%^y$Y~Ao%CuTi0DrFxJBNr@xb92f8=fmpY<@MOXfG#%MSL^A(Nf?LCiQ- zMrwwz@xrT#M*KQ5pS>f)OYG5~ao(us$ca|VZh5{ng+E-g7dpl-bWu_>-?($F^>Cgv zEo`|f3FM~t2tw=;C1&*aZaswdLw6~&osV9V)xPs9MURTC6$C6B$=-3dIGI*w?pf3V zY8G)!Ve$6IJ}CKm|Dwr-Y2C8Yd9^9o7WTl_h3`RiH!IT$%cF4QkmM*x>gjQfB5>&k zMxc?zdl2)+d5q?MHk-@jF`eSU7RUN5a*%gCx&DTH_ZuQLtzi<)7Zjn-I{4?lrRr)z z>aJ#AA(lznm`6N7km4a=aCd$|iDcNki0zak2(O560dtqrwS`#5tzlVxFVE~}xyS?PZMix!)3uqnu6LReow6U(+C<8%K5Cf#m@%E zgF5&fP&dfh{2c=fq3vLHFm!R2L)z;%+pUwvN0MC4wSP7~Ly$LjH)kUi&W<@4E275T zS~etI%#gRheDZf9;?LwgJRis(Hs7P*h-@aj)5UL8`j4}$RphtfqALGUKKb2m)Zo!b zFnWlcQ+ujC))U*$=I1_zYU*hzo+;_bt58Xk629a^_c!?z9YQc`$)+B15$dlNzN)2J zy#_s!J!>OKcl{LI%s{c((G;-z9kDllC0zV8u7ld@w6RHJwZNg}=&_N~B23n)E#ddT zn!lyQKjtd#MK#ljq66KHGuH0ZEY^fDj3>E}vu8Tfa{OrPLMUvuqxfCXde5sO-Aun`nT+CM1-Hc?Kz}8tEjmt0g2Qy&f4c8aE1?ASWePF?^O3^R9~CU!KR}%V(N{ znWHzZstdz?*1fA%YMqVUuGW8!HbqTOf10GtN7cECtQKGo-oEJq_315kib`m`PUEDw zv4o+wYJ(s;Rx^Fgy^*pW;}O~PX##0U&O{M-=Sy;ZIbK#W29?!&Vb~8qd#(hvSfn=M zs2EyLI$HyW){j?3!a$Fthaw;q9| zpQI!*LPT$Sns>N$zYGS17;g_(9WZ&(%#2cgctb|q1x*MPu~BV`CurlXI;g3s-D3n& z3_ySq$i2jh3ps37mk6uNUXE{Dq{T18&+CCHHQ&FyfP+FocmRl?u-WAh`b9gWaPH7UFLrAdMS|%%w>+qDUv#)9_eI{d+lIhA+Q+h(kpv@%Ub-dAF%jzYX)i)$C5j#(Kl^%tC`_I! zp&2?py0Pl)lPD^hgXsxf!L+gpx&62lym`nNdcB41NE^XWvMQFC@)xH`k56LeZztK4 z4;+*M^|pV*`O0^=63U?UOSMlS`74sEV>PZBt9Gr95Nvt1!`}}UwQjK*EWuVJyE#F} zls3Wmg5>*r;5(g6XU)(0lp|dFoE#8zrG>@eUg^*&uCzLI0n9pO)B6Z8afUDB8>t+m#2-VxS4}HR7pU1#MZLLrk z{RuJ-dy-R&MKVxv5amuVj|gal6qkcy!wY~9vrl8RQEMc7KBhLYN4Qp2Jf8Ty{E7t7 z`nG->V7X{XyWXj5EUCMsbz!)~79v1wXS(2KR6Sh{A$z{!rv1$sG?`_P00isQd0@%zsXz4D_C_12z7c6Z;&x?2pt^?32yTIS_d=i#lJX6d5W ziB~iR390wamnY=aorDx6Jmh^pe0XokXLZHEhB!Rt{Tq;fJ@*#!oB>O|Y)6L>v zl?>7n3U)3jniRYmdXtMd$`1JPD~caF|BaYzeVQEK6?YWF|IB6Nl~-j9JT8rRtNP;% zB_6Oa+=e_JfITY11`_eHALez7w+tah2q3QZCaFpX;g4xl3?SZHllA%L3Y!4|7XG%9 zgMbfi;)Q9Kb0UtBwY{)d#&;TVA)}-%T%C<#)a@>^m-C4nb7%0dk^2&kNyRirE(u-G zD|kL<0U|$}C&$jtF0xlNx&L=TKc>`&!%$t5^WZF<2!_{coQgo5Jg}UUw>huB*jZxB z^Q=pcy*J5V_uGR8s5bMuVl{LcZiD@Kc|kosjrOq!Kc*ma3bn^{M&Us8axn zZw!;XHLzDmmNwgKcrPqUM*60gFMmZ{ObE-nk5+y2PacqNE| zc)ZdMr$wDpED-d4wx94x+G9K4Db)!>HcE@r3qpFv?!~58B`m5%$epS1!PcVD-5WZm zYWIr%B4%D{>A32H@`!iBe`-1E2FXVfZG-x_N&FtzU5TK|(`Z{BvYw2LvLAXODwrve zGVjpg1j?ejjr%ht&G0}9?siO{t5gZrIN~TYUOwjRi)*y4Mm)BJK4$@dqGnme&@>I0 zS`QDGoyk-QM^`u`nv1*j{;R1(v|teKKg_KI&5tgc_Fn`>Nv2WBP`YR6w{HxjL|h-1 z+uG+t-Pv>K#tkr@&8<+jC~1nzSfvzWvb#IzhZ+}TlbdG^qRa4az-llXxjGNtE3`3 z{ZbHu+3!xK)0y8lRvk}b+&;j631$~T2j3N-?gMCG`>isd>~=MpC5o=3-bh1PoesWu z2@gNofXxsO-IL%IAr}72_~y#>n`YA%Y5o;VzJZhDw%=z7#yVz-|EafdNGp3Z9VC=v zhOY!q_<}2;;=Dp`Qkz#+V=RN**#cGjvLR-X^0MZvXNS;{VO9v7B{5j$cA@-MeQb>zCwH}s><`)eQz|B1Q zzYpzy-!!y^vb%f$LgbP~Z%a>Ck`Bjcj7Gf_kwE1A+8J1-w|r-5OT%kOcup|_tvQuW z2P933exg4OhplwYbQ3F#TS=1fCYyY>?wequ&jz9WcD~e)-+?E!9j1={3#opF_Xby)Kbg+95Ru+!PkvbO6pUR^zlGEu>fKnf`f1GaDiwuEI?`>Idt%?i#98@* zPw5I+r0@h~9~2~Nn=HNpTA%Cit#^svqzx3SISQ@(j_aHxth2UAMAygFPE+6H7-UJ? zT`^u`DT$-$l{HkqZyz3T$I9x1=tVbs&MPP{$J9;B)Fw+ug!Zfwai}OuBrvRul+IMI=V&Wp%2m={+M{v>4TH&M@iZTGjY!Kd)_VIpXBRc zAyXL0vI%OfQt<9RQ~vv1MuAn}Bc z#+4Xiz$S!?HitT{Yx$cXmPbXg(qJ7xV0;UAr$*HXNi|yttye`RY;7Bsv11CL@v+hE zSAbQ2lS{b|q{Hzm8aeEcQ`*GGm)&c%YWe0z+Z-YSo}jWoq=ZoQz0mrGyjEp6Djw(Hyo_5-zE7t(bBb({?}7y=NV{KT6z{ zjNn$p=MY6$)q=;dZxh#a*|lO5inJzp?;3(u#8zDQhAN_W=r-@WyuFlje%8NJj}MU` zpM=(nqFt+m-aSPpif&7&Ae>KJ=eHl)?OocKs_y~>=husic4YMX1gDcHu)8P>(~yoM zOlgxJJz7VDTgIZ6coW;Ms)92m_^D>ynjY6#Y@*g^fMubfdM($84zJso;t&y`WDVN7 zlPY9BulgOqZvWx)Z8^pnE_8o4eG#lKeOaiYlLklk1HM>%;o-7-pln~_XujlTg1F9x zK8aldykv{NKYl#&TAQQ>ErqEmX?fNRal_pDll{ic@0PMen!^Gj+)zD$o$3Mh=bv|g ze%Z{2HZE_3Yu0l(2(Yq@m&#=O9=`C?g|xWH6t?V08X6i}O>XFU#q7{&$zGXBz&kN& zAV^Was@CN$J>w_Y`{Q|eyYxc%oPaw4P|`4*qn5z|61pkZ)Q?rVdJr1kyFR(kbMx{< zoIr;tvGD6n$9~k`7sEg`%SGuMk58~M$Gy(-)U;t}7&0Ry`^)3a0g}D8@hkP9u+QU5 z7zTBJrrvWKKC4%x-UbtH4ML59R6>t&Ype!#I_wUJ_XzPcDJsg~a# z$L0|Ca^&L;p^8=zX}ja0>K7jVYbxB0H>kblAsP+557L4 zSg*DrsQhIL+;?@-(crEuLy=w<3L&yk396o^$uX9NB_uvOAB+XvzkTN+uZU^lMGGpq zr~8)}ON0l^wBVUyFDfH_gi>`|G-k3-8w%_`Ru@GYyW)QPiq&FD<*%bqDCp+NUN0H! z&eZk4xX`~bUg=jrW%8NZx$35^>8xp)PYDZoMtIc=C#+Ta2MNHL14#!m3+rZ-n`yiv z;WUcE>|rALM&Wm4WUfBG+Z-X`eClOfu|{;;^jeemN`Gbf*XmUaEi*^1^GLwc;8FM3 zpqX|OSh>kX!B+_B2j#lajq0ax`qkV(gnJ57=+(G+Yr12l<43E5?cbyI+!zZ9?W5K;eY4spn`UrW(55*s+PbO-ENM^CbhrlyjANb7=p;JI~GXA|JO z;;ogeKK#?TYPg0;LoZ@(znG0*17eQ|kiY-gxaejhI#7zwxcK##7_V1if1F=Qt;rdu z-~@a7BZ}u6*F5;9oB1bCmg=?fdRXg;RI23*-2zmI$c`VQR@<;tQLvbLMx=@e011DV z*m+$OgaqMi@z<%l^xNRH{2*q_2yN{we#Xac*vZVJf(;THyBU98M}*^TKXPj|~%&K75#dIxrPvA6fd<%)6=KVK}F!424f%(Cp3Kg7{8Nd^K?MJCNU!wkc}K9nlR8&{UBsu%AV7E zc;55ASpuI9d4BZZlXR({I9+NWD;cKm;74RLb=mIW(}&DqD)h7=M@*U zBgM2Q)4TQ5J!FlB)o)$o)lt8cbY*aGoPIMHxHnM`W2K^|_H5eh0K!%0=>jP2-QnW8 z%AYFOk<_6Lw2&i6z`&euZ>crk#1G;TewqrUs{h3Ws8vb%$@_a8Pobqz_ze5&p34+t z)bGz;Uz(+f3!?1jrf0REvS#GE9_@af{dniK$^+=Jue`&+F_ku*pn79^mz&y;H`?_W zyU~o#Fb*SAI~^YsW*@^wx|EL1Hg#9(0*>Y1R6HIR!s-easljfNH&s*)E@f^@Mf@v* zRvBji-}iQ&uB?3Y7dK5#H(n863&sX5VL8-*ls=RcHVW6j-upT4y@5pIg!RT|877Ml z2I&2DvNE%O5|zuQYC)$Cwn9KI`K{Z%m{MFckKzwaBiK>0b*Euok=&2k4IfEpj@zulzjaW5N?c zzt>HqJb!Fulzkes#RRx;Pea_e3ZgmCqxv&9 zOhZ5^_sLws9Ug;f&Y7IUo4@+AoO^vG;@lUEHfj7eJ(j9snP8iZ{v>;^D|@6PNwbt6 zJ)($+y&WP|sPlCe3Dahhn)TraY#TiOI%}@sQV(LMZu-$DLoS!uI$9f@oRB;XtJY~8bwg?!_g|RhWQzN>l==x5MW<6m})i0f} z8i%L&+z#o$)h|@PBcOD0y>M@iE(p5k3qQtt5?T9pmIS6fGi|$q1w5%w!KHqu|NW^g zlU^RWt+g0P@`CFn-#KLsSW{&-Y3bY9AKCH?ZV9YpFKDLgh@$ON8@VQ!uP>M!%oiaF zJF zJL2MGIe~|~09-l;+_q*$iuVKdW`9rC3*YL?i3bET?5cD5SFRhYV49|Z=u*V4d)m(n@s)(wDlA1ZEI^)}})KbhtD2ynN;X`dzPEjUDkn#x)*eF|Xb z3D67c?zbL&iAh|*4M6Gsh&`|_UG*|f#AOl-XE#juxX-o*D;@6PI7)Q0R=ofVEVk^5 zeV@olSC4O3|L+CIM~Ivg`)?cx%F2XJqww(SEPl7o14CeD=UT3_e4D#`#g1d?99--h zR7wlJVU+*e06ny%7js*dOdlAbQ>kFZR6%pTc!PXR$~Vp{-`{aVP$xkBP@~47Uj(!r z+sVgHxc&G+Clb_WChjsqm~8t z;SGk5e}MUO`SIjnGy6VZ3@Sm{6c2uLsH&>x_CjD!fBbWK`+cs*3(%0XHB7G5y^zdf zOeKZ!5orKMnPHYn~EZ}OOBDV<`8NNeH6)woAne7`+;AU$Ja#z1<;=U`MKH)O4t zD5@TgEO`oNX3o30kcwNCbuw)ZaCJW2p%z1X{=MaC`#%gmk0zl!(Ty8wT3Q3A;`MA< zmzN)au9BzJ6v3a~rDn&){C!O=_|85B3A#Z^UU*38N*Dnxm6&=j@*BkWb1t6%F^Vgt z7FARWU2(O1)3JhLP5h&y`wsua)`TBqnVFt?p3@wj2LJNiy&9?f-0b$XW@%NE&o+jw zm#Oq~Q1v#!93>NK@_j(LBOgkK7h`|D_Ghp#4|W#hdxn8k`7PYdp$345-0_~Qsz%pb z9=T#=J)@)(r!}+Ev)9R42c0H&Dy=O1V8SFv3PvTCw zK%;px?$zdEJ+kwM;{w;5IW{}5KK*~@TFPJ9*fMZ$&wL;1@=A|>x*AI6c-~TOM)bz{ zeZodALp7T`0UVk}EwR>P9~6VHRR4artJz}5351NoT4{WOVwu6m*y|E8S*_)fBmIw! zpER8oy~E))wtMz?lNs+@*$1yF~ei)tg!Y-^1K-}x=$)yXYq?_G^!yCh@VftdqAx{o>b6sRwl9WCd)iH=t30;D}% zb87t@^~vAQ|1$qzRk6Mu_Dj$L5f<V-LKN9a_-XW%M#Nd-ctw= zIa|(I3flA8hmfv z)ms2n)vkZTbO|Ee-5@R9At2q|Dbl&=?iOiKN;)Ms-K`)ku<5Q%NawdW|L>i1o@X}W zjLzV|TK9eBFUD&-1)xt>?LKKr8g3niEi|ZG0Y?l)tF!17DEUD!N~)Z}4%!a_xq zs(zTSay2NHt&?zl-Ha>HXxNLYJDuGqqW`1(P9e@fd9*ZJP)084RvT}hmS-bbgIxcTbgqyRc~a@dS@Gg zx;Z#PU#SE5tm+&3Fm*njvj7?~A9&R;Ffr5JDnM!?nPSh-B@ML-WwLZ@G{Lpp%gONK=fGWfeXb*s@|_EG6#s<*UcOt$1Xa_EC0N(0}WRpdRW zP2KA?nSNH$m*|7tTC+`FPS$Vf8Or#Q{|+Ogo1LwVB_9<&vCDAO9;Y`>zI(B`*Y zP6|IZ@>*C~`J=#6GD!<6oFVed=l|#kSuiiegjZXho-JzN-30b)fC9wGk1(F*Jn`#< zf{c!4@21MVIlL|T_*Ni1_nnNvuC@W*ZDA3D$UaWHgnr35~-{kJ8O&DNW#Sg-Q<|4KV#F<=5#}i z+U(y_yLH(9wJy~xLc2Oyj!k9J9_7bBD+S~<0Mr8!_Lt;Zmq#C_kpU?bC{jG}y0j!* z!1FPmw2VxFUMzm8ZdpNG9#-lJ6BE;$kPgL6t{c0**7sf;0&2I$t%|k}zBeY7#*Ju^ zm}JQ{u_Q38Yz|B;#{}$#2v|bXmELZeW!iZ6S4XK!^t80Yo3jAeKCoKPB9R0SFFdV^ zEp-TY!=!&EYOt)uUpmYt&qa~5|9$$<>VBa4{5l7z@^3&+fOHW1h}lO$xWPHm4x($k zJm4`?X4*`gUv5fd&~T3iddASGGOC1UvULJ2o54b=}f!zla zTsw5jk?w`-^7qTje#9o11Q>x6xv2mJQ;0+!B`QdQcI?43{^!Rm=I}OT-5;ZgFHOH9 zl|{_MdZA3dbyb$`#i1mC$NZ20mGf*qcOoZYDa1^1z8-hEc1$X#KS(|z^ch+AZ&@4v`#IjFE`*WCF-{=l{w4KBEz4&vtKSZq(w zn4kwR3W%UnePFvk4(kp)x|h|?1f1A3LvazMPY3HZA$HLe6o8ahQkB_!`ALpu2Rg^SK|0q&U>BQH^wO$_BA5`3T z0P)kK4RvYMh3_uwE-@9@ZTmckKHlelFeK%5{1c?sXPg|5ektWhQ^6{tjH9)HGK(Iq zh6k-?S`Z`QgPURnIVEnxhWna5&p1E5lsdnA{lC0{rZIsAMx*TVKaV-`NzI-c1~g`s zqv^5Jb(kNh{H{ocdjplp0SIU8M?9nT|BEVN>_-^a1p9`Byul_)$V*zQm!+bCw~Zc= zQ*yVQS`Z?X1<*@CKaG~b|mk)3M)MED`^5k64+ob>3R$no#`vLDx5!g2@ z;~!{yw%yy%O<%g1lhVx(G)|8?T%%-G3xc8u;xj-B5F47M?YsjnC@DkeXD-90|Lu)v zRS8jZqMb2WwGp(cW2K@6@v!vU_wRxzM}avNDnRx<9#F#5aEWrT9pmw2L9XH7f#{Fv~oF+RXK}*qipY4(Oi9B6#)5%|2e^ERKXCm>soBAQr|!! z=@fPk=(r#}f1M{oQ(z~;epw21_qA}MG#p~KxC9Px1|bGnB+K zSG@;D4HwJtS}*s4fq{4X#fB=N$5gMwAD7w+l|2yp!DXEnrx$uMZg-u&4UExh#c*=S zfKxt)(BQnv7~ZClT20&LRIkaI#rpG@ByY~s6{OHKhJm5yWPkYe%+aBozPy>+sWT=M z*qEjNbJc|_3fF=K1$ZT;^NnON^KU`pD!Q6&Cy!RFR}bwwITYD1Y%f5H>*X}1#1)sU z{E0hwFZA^ORl0px+se^O_4WE4iDUOTnGmPr#m5EyS<&KyjDw%uF`<*w9BvTSMoSwk z5-Q$+2?;*!B4_rDT*UF1|KL0&!F(}M1N6Xt;`0{tz>{@kyK$b`TgTDRiGi)wk?4e7 zd2nU*&GnrRn`KAmm9_4?lLrT8xUZ=bLo<-A|2shn4tY<>*DF#W$u#4e_ox^Dc_e0P zKXj7L zb-`8f+XXQ&H)BQI`r{|mwwx7)dpze4Q$K1{MzfpCrsu?CqUEN#6zx{%Jkh*84iBs~ zwqRjdA@9T6+62ngmK_ZfIx3bt!W7efsaG8qSD3Fn(9nD9X%U@XnaGTE`Kc2)WTH#} zU@Wg@0fc{ara2ZbZ$XV`tZ=1IX9UUT_UuinILm!t4n^b+=Sw~TqCX=fwbPThmBJ+5yw^>r!;k*&B06>arsWh-v#c_v^8yhp+a+@xkam%h53| z5ku(UhqNC%edwU-e|2#w2(CbE^4U7@6IsQ36dj%4{JRF+aiHk}PPw&58&jE4e|z1m zRd^vA4JdVhWb$1aExTSh?n=g86Dc(HZ1acxnY!=ClchY*+PJrle8!E;%(DsQ%qt3y zZOwcE*h^|)f}G2VOrS{zBrI^D?=AAVARaqDfLzL4yyn7NEixSxP#L6yxauI6n*i$B z-R1#zM_=^?8F;`sUCoexlKRiTcDKBN0_a}Nx5mW3nCE3?ldV3dr6v4|eC~+}Y@!t1 zcdsS{QY{(@2%w}Szx)mf{k&fw7#B^~+OD!x}GJr_11f{HSAN<(~~7O zDi0lpPV@KW{jJJGO=C-1v~g$YAM}Ps_v(M+xVU$C6?^}=)D5`v1vD=N*ynLofV02$ zK(9*w9osy%v3oG@%bH5+8oszI4SCoS=w3q((;dALOd!~(uyG|;Yo4qHNgp_#i! zTjp{#8SsZA)WZM7Z-lMO2xD(nVfL1U4u}GN^8&d4i~=-pHVWWa!c5{K^V0__Kn0=e zp2K?qQ@u$ifjM@Gq)DbNE56orSMcXz16rgqsjT7;MnfYZe4Uxn#19IceKkV!HXnj| zA4yr?kVhcE(E`>aLJ-&c;~T$48{PJgSW{C|aFD-0`B>z7jYd3x?2?)DZDfR4;J7KN zSut^bWgr%B=rJRhx55hKVz-lrrSC^RQRjw}4ZG%wRvXHjS$v#_#@%78G6l$86YZwefRB!n7jGi34~^qRW9}uLrb>pfOFbu8ZQ0bbuJcHT+N^kcM+! zkP3w<&gn|310Pq^xSAMs;?=hg$+afg0vobR1qGmU*3cu*-4Bhq0&W%R#&IlxnEdZg zTor(h%_qBs`ybbE3o{syze(5WLICkRVGgq2At9@f_LYFHQhG(0+ZhG<>_gRFb#}j> z)N+>Xy?)HM;vRicf18lPs3$on85R*Hx8DvrF}`;Ox$lPnCLP1} z)+a-ke2}0+!6yQ-fai`!b-+zHjnz1$P&RSr<0!z|scl~e$j}X>$LR}$Rcv?wC`25~S$wu25X?W&K1BFX&`%9zT)&rsi_$C^x8XW$-kPcwSkHzkdU(N8;hK(yEm3BrdSz7O|J zi&vk;(;gf|SzzPS=5{Y%Sngdh$hqfLZyZ-mwBQ}n&RUK}u!y*C(S0raPbI=F)|VI5 z!PZ5@kc@M7cTS;3E!;~7$^CR|Vt)%_FHeb+(ey${J6Z|LJj5Tfzg93WU#pgRmT>=p z&4{m!Fg^DWGcnIPeqrgx0*QQ0k`P;Vy03erAqY5jVNGegrRYbKx z(d4iY;#I>8Wm#&JdeOh#pDsBadobZ}8YjIstjB?}>NllKE#6bDD5zPn_4_W=S#1jd z8#RoAEaRU=nj`AxHdG71;OAPSL}jzhsq-`5?oa~xz_$)xBlUOh-r;JMYrp?tKJWs4 zpZ7*UL>$?S`YYmf3L}l!#snGg1JEDahmo(zftih={VUK}7XQv1tHB$5{D=(K?X7kT zbMr0Wh^-u#`z6N3Nw=JDQ#}cq0g}jX#A0`H7*ukCGGqZ-JY|+=adGiYyN>sL^!mq4 ztWyTaWfSmY^Z`py4!1#cT6+2&f&LH{h=)uZh`N;kuEM+gVih_i+nDcIeE>`Db%W&& zu7NQZeQB|*dpXx5GA@lHi0YhBj ze`eDyyq8l#Q`16Fab@54^HRYt?0&jRaN`hT?1%HN4=x63pH^dbo>7&|jq9WO+r<|0 zg2{uxPw%?P;ulTV$K$Bx9~lZEj(~32d)Dzl;Da zW@#y79H9s?K($Ps5-c`E$5vt3nBcKf?U3=1PJJ7lv}TG<&Y}9P>kTxuLR&)r6EQGA zRLwU>1Tf>|xdAty{W|1RnfF{zL|iLzP#M1CJ_F1}O9T}!FMh}NTk8f#HzLpD#qf=F z!u5>*hXojjk&v6sINt_flJfE~evjpL)q_BZ*Uk(B5KuKv!gn|P3Jh{)#?ZSQtPmk9o(nHdlbK_qD&R_hO66Fs8l2ftePr19} z?>GO@pYMc&C9w4>GuqFnu9W2EM=KIhhfssW0RqrTr^BIk`}KI+pC1ke-(s#q=pg$nsSr;6`fEA-zNBIB?RlT)p^H(W5 zKbTjlqHhw@euO2h0KzCiDhPZ)bI3Rh^w1`|E;`_`iXy5Z?#ix*vw23XoLnN=!hpFJ z=)uvWb~8!3ClQ^+(-HO^fNk=|aB&3U*nhv0m|hN=2z%EDZE)9*2L=Q1_1&5OZ7yju zUpqSc`2A0wbWBHI^0ovr$u~iX^KZL%`h0)y7wmZD?Q8Ab-LgcfoKx^6ldJ>Q4e4va zkra|AP!T_`nM$o!{>bRvzy^vj{Js$IOl&T0PbgAI8+HKBNAwp4W%C0Arw=23w|LJz zhczA6I#!8<+{ArIn4bj>q$0b*JnZH-I?7Il{e7?YLusC$VnHR!sbcBOT4-dH7BHJ4=+;}7g=t@ z!R)}PTH)3`P1t>snbh_zFkHpN9&z|8w3&YU_w-~v@ODHj5&;eiYA#4Ext9*au+92? z{?AEC4BT%4&1qq}K)hW~fmV$yN7)wLfup+UGB0p=+l@Usu^{ej#e6rQ9T#m&QFa)l zhnwr&5C^O!d6ay8kC)?HqLd!ye*y}RD)?;3|2J~b2iG))wXp~-S2{Mc%*PLw&V|o2 zM)1|mYdysIJ6y>l|Ly!JR9MbZ{iL_s2Ylyh8VV*b0R5kX)xwZ8-}^Yle0lmY){fJ0 za$Yykl&`u}jRN$pmGpiyk@77+F8jk#lsV_&hp*%VK>`$;t*uIv1@dI*Irp5sAyORY zS@);E2DdXhwn1GC=_KpqBm@jBMaWmjhuqdhfbj1{k(pQN;iEcu)p`1tddT4o9@Qa* z|Aq_t(LBYn&s8@dL}7{49srrU^?Yzz@q4RV3j^HmzIkq&4Pfi}g?-iQuhI(yj2v#_ zRQ;F0>;hoKA8t(m6X?E!)Qaz=_`M>G{3w9z$Czkh8?=R_NIw0=os<&s?uGnUXxUH0 z&xiuB#xu6&Y1v*Ev>Xnn8`oWT)HxU!x)~wAz(p4gT@W|2cn1Ecu`ktt+VgSro7!gp zVjAA_7vs#R)(!_R!a+9rw=pmbcdO4q9Q-n+cm`0 zB(UBjzpeM9FcKlCX}+P`&6y6+;hE-M7Fp*0+j-B{-Zm#ZLDt4i zR{m@xvQ}ePeiD}azHaSSAh$Qfqf2tXU|@Z!RcSqBF@9~pTf+>xXqBSAQ|F_!J~2!3 z##yXlkjdHQ>mUN;?7$OxS?fMcoYB5#x4D-L3k3?9;7)iX@g?4C?|lR5O`9BV`l9Il zxfX8NYnJL(G^9%k806&!T0+=oxpcPQ9d-H5DN%BH zFdxZTD5HieI5%W?NW#aDb-X>6UbuH9D&m736bA>b8W;XD)ig{%t{h$F_y(Lv${R;L zcNPPfxnPn9=@@_8oA;QOdamF{e( zcG%T%=T6@43g4P<95!?IaM05IZ5?A&3>8}xJZA0hkU5ojZ}pLu+)%n+Eeqy1h!8C1 zhnvmB)A4L4wbo+I;)f3^Yiwa|LGk3?j_Fz|=$r5$;cHVsLSjbyn(eFEfUDtdu&!58 z4W%G~oUE_8i1_Re&B$AHhy@!u+lzaeZCMaH1QFpHSVTpBFD7pK@+N=Z(y*))9sat( zos>7`U#?P7h2rJGLQV501qOgfMfn8<-fEQrtOmYHWMf;C_%^oHI>j%xkn;ig-X5Ar zvynk9XZ@lq*frx)aeCo^+pnik%=+~&COM2QkMc<1&GjrAnZ#l#$*zZuSEHBl1TqoV z2lMIbg{JyA2+(GTC&|UhGIEonM*$zFIf+~HHFSjx8dyvA-n{F6Zt0s|2A9=5bUDSq zFwr?Z*!K|$qx5MHi^lBQk>M+nOojQ@?H^8ywaAlnX*keQei?P7FLgKB^sunN9V)-y z9>>B|r-tmG4mSox9}nz{z1dfyw>K6U`^B?850fa(FJ*9XdP{P_0w%ezoX4|IA4X7< znSADScFybhCP$@g6vElWd9uJRoP*g4r#NR!c&L%Dd!$xZP1roWQbDNWWWwa;VDeM0 z(pP!p)(sg%(Z1X^_mkHY&%0tA$G}QfDaWJ73R{N~9lV*}L(zUueydSFH9U-8EaaSs zw_I)IbA5Q@zLPx0UQ(l&KrVoGz2ZS|bf$iOgM5+Cw$s2R$tm!m-H_`6Mytf=`Pl%s z6K?0^5xt4rI2~N?jPW0LrG$F8S+v=3LAt{}^&3dEh0fIQn~>|j^=&DJ(v`kgC-SX? zVy(-*c6R%gq_roJ%|f?JkDxk3m93Boz?7^4|JR2DJ-3~m@-2vp>v^4HB?)tPz{@8o z2NQ6C%~lOsEgZRpJ4|b9Eg}Zn;4{KVv9;mM9sPkbsnLbg;j>3&NUuWCNcTtXa#%vy zoqxh16?8=~HW4P%c3ed$ta<8!aqg=1w7Bt|ewBaRhGY77<*N)VaQXkJj&bX&B{(VC zo*kZ>FxA}Br5mB~*v&^sJl;+Bd_#ep*e&6U-mQt9{<_##1u#?K5+#zGgIM-8K*}nt zI3sIkCwndQdL?o-;1Q2ms~WuuxM$yp#J)|ng~47gJ$WLhFsdb=ca7=Xza}%iPdMf{ z8%SR(HgL?xWs-_SM~x#}fAl6Wgqcvz$P+f`_>`KiPLH*=0%%5ioh*Y)&p@$xiyvlD zcFelQ0`-srJJx+Z{;8!Zs_21N19YxXJr3sRmRv&fEz-w1EEe4}_&{u$mAY6@SBYIX zH!>(DfQr+{`A!iBNb5xUP79T&1B(-xyH1wO+gvmH%8T$CKAGsHU%iQO1@zIaPwr6?tc9{a`TfVLEQtV@ zF-h&sli`v6A1zA(`{Ac>Mf}yLC|vfNYIkVM^GT;A(3eP_3@l9fzEfr<>SAiqdL}z$ zs8*Yv;&7f9W^N4RTv|@O|KP|}4;-(@zBInOWLJbaR;GnNtI7Xy9VUI#b8`4yn5x>b z3<>(tzNG#M&MGD~EI@-n%DN}S*=Jkd*{#rRP3}r91HATX^w_xkBPY1%OhC29Pis%e z1G^}t2$=QC1#Nph$)GZE+cyJv0i>XL3 z6yWZt@u5I!TCVhU8$M$7T$&HW^Z-?cfvt%U&hOI z2FH1_C|z=vSJH73t9FFr&xpis|@2QG+uiKruH#WxD!rjJ22pCtn zEma*sipU{d7GDicz+b5^nw?gW+-!bD(ErNr0VrzwNwXRxJRyKnOZ3H3vl9iGeatlg zScA(@FMdwyIqiO4RzR*@c?`VnuD~%lQy(=o_WZg2)Z=@iChhyx=MrYf_$|vT>wtUK zoZDUQ4#SJmW!C0ZnaC=01W|H#mL2A@fxgsab#Wx<%_WLVU6-H>H84xjaUOm|qb`fE z(3B5cW1}V{%VX{Hyz^B?f=+pT^*ivQ$>yE$xjIBH$?;)+o54pxP9Z#u{8w{Gg|(2O z2uq3DdB|>D_}L!4PL7Ki=79AU^2)|iV!AiIcNAW%TLpQiXVQL`g{bpC-g0s_h-j9U zl7$5}QkRvB9PIZV{AjX|q{og8Ey%0O&5fAru=?*DWHDTV#`krsiEyNhU8BGVt6B&7<}dK^^65+%dhhE^mjxL-5aDj9q$nb< zSA8jp+iWfR37x;GXOalljPIY-8o+>Z4)wTg0MC%FO^3X#CXp6Ih=v{L;VsnNwTyK8 z^@3`vJJH^94TWZf@z?QpUjFBn6T&AjqU_Yu-j;AD2O?sP!I3^uT_gpIbf;|JtgTW) z2E)fgLuE067Pn&Exv3Q^L;n%X^PddmS}eW}_dd_hT5I^NJ8rJRWHXouR0WigZRw-JQiU9nl)G7q0XAYc|TQmCQA$aXb6BmMq25*e^ ztL1mP&0{0kyIU} zFm9oLQ_0aNormB989FbxwjT*v?i0g3_FqG=OvgSaAaYAg)cO*Fwv~W~rTv*J$S3G|WQSF^8TeyG!yrF>n7#nF? z)xVVJsBx9juWeiUte+i2F6DZxZ=`&eCao>#SGgif*E^5>MdntYHots@n%6sWM!k+Y zd}i90cEZ`o$A|VEVMlFY7kr)4>dhY#OrQ8%ulE6y(NXR-bQt=v_mQSz_P>l#4NQwD zzCy{84pMrXg!deScHv5eJ|8`$zrV10tT~Y zDEaeO6cSITM4o;K0yN`Ghjx`dhwoga1saA0#sj8)>D36sba18KKum8jMz-nQ#eVIw z66^(XD(!KEKM=}molcDJvetD?fS06}7?v*t2Z~mjtue_By8qlKyY@pObS;u@wGGj* z7K%lL2Q!f9nohkW@~1eM;-D|=Y#?q4tLuR_2-5Hiv4$rTAtnHWQu9PBNaVTm)m>7= z2TIQF=dhh#7!{4^8Iob0B&+i94KDRY!*}Lx?lWgO4R;OnTe$B>iBsLL>{aFL8d&R) z5zM-ZBcqwdLQA~S3XE|GuLh|I_|TwBYXRZ~W=h9ZcmlTgB}MFek}E+?Htr^-7O*9= zG`L$WpYeEj%U02kb((ZO38=#liH7{7vfA`YWTb&V?B=@QwqGA93?L>fbJGw)qrA3y zE!`9=yD}JraO?qd0vpg0$E&)n)AL8<57f85KICgePVTl1u(TVxqrinw{L7Fx2@TQp z6&NRNWH=}}ygpA+8}$zJH+`x~L^esTlImlLQUz7LxWq^8OUpmLdz3}FnT-cF@?to< zqe^S!C4p>iM|k3ay)&E{tuk90B3IhZiTkDE#xT$eqjs(Z7@J%W8yQNR ziO0qM3fWw>!J#5z4;!DTQ3zCmlHh1kg^Z?0E;A>yV7V~|Iu70%yT}eydH%q}%Kekl zsgZP*_(JZ}w5nLbiQp>r@W=Twt8X=4fhL)U$cHj#N;Yy-K}Hmm9#|AlLXS2YzpMKL zX{p( z$Qv$eI(Nn1=frHm5yxJKm*IQk{$rHIG?I#^TbS>KLb$>|qgXB6ezCYC9B5is<*gdA zzE;Jn#xd=XKH0?H4UDe7-o~!parjT9h~C6VMIQtpmnDz}#WC#D)WTUQclrwpjA3bqb8+*&+QY_JTCWZvj5 z_n;vr9gi{%-8tW{uOjN@$nsvnl*TKB9t--YB!NERe3DK1)aEj_FxRYth9R**#g4dY zp?IWQwfEk z|C<$R;cgX!EifP&bQJK{noJ|dwQ$}h!R>Gmi}!#Hca=Qp=g9_#XUd$w_3+w@NQ!)w z33B2p_W-(wZ7fn&IPQEYO`~BbH`|^02iO=`SL;|fKA6j-3@;kXlJ@;OEBLzr0i61{ zKIK#wnYWl-B0`J=ZWcSklb8JsF+@e4(mb}}Xk$`)q>ByD9Qt2OrRYnKC=LBn(gFTBS5pfoL^$k~nkZ~S6h~iC9t(P>pr}oBXe11zzm>#j&aJ&05uV;LGQN`(8#+N( zrkvGEi6$;|6D>VIi@JzW?JVYKF$R~6t(x*wHJniu2a|620iSTHByI@f4n z5jV74fKem%rFSm8w!xPZWI*s1_hCwcb{mOX!qZ@&G0CR4sl2kXmaKX5Ur*6c39spT zz#fbzFPwD1K_^hUA7&-LOM@+wUqfkCa2`p_O9)vl{w&K(;Qq5#YEJGSB*Jh@1NX<%t^o#b7pN+4)KdY2=k8v4u9Z2^Wl9D zF_#f2Qk*#q&;57e+Ak63>hAb>tM2@%jU~@?vUkWz{GQi7a-eWkjLqMz1UQ6A0chWa zoI$vN@~?*jnYwPLp|@9q4X9MF0{`K@k?Hi#u7;?z2&qbBu5b{1@KI>^b|yoMy>8jc zTY08Ig+pYVz}L@A@Xwdwk9#KZkVcN*a-PKNqb`J%341>&m*&){Z4DoIOEF{a(plZ7 zz54}`-&bc3`zTWewlCS^%BGzaZ+JTn?A-}c&koY?ys;ClBD*^^uCylcDWRv;b+Dgu zpZ=5wvk%qnb4AoUns=KU`)J%eFDiN;A=e!nSG!`udPg-?40aMxianw*gE4LjAGOUh z^b`Lc;xQW{uGzNKq9Y%A97FIaK|*}>-t~i+R?lph{e@W@1Yy@%Ws7XU>R(3B!SB>4 ziPnrM>nUei9t|0n^A>+ly!Yg-%zdJ%d~ja*agyz83HVAkXi`4Iff%BFJMJ#?-R9X| zmH>oeP^O#n`U!N@2A(7`9B?cuhPg94+l=$ua(Su889Fl#kw0Jh>EZ+MhRG-x>FiS& zEwE2TMqlr?kc(8VnH{#O!nR{fW>p|Bl-Y&CmS-0(DIG^FnIc1ae9lb0ej}9_hvAF* zx)Yw z8elt)?!Z#fH`Q$G%XIT7+k{=@_!pbVFS~stu-cLTu6Ef(0nLjbMuG3WWg8?>Bw2KC zalu``;+g(5+w(Ej5oHZ3Y_;|+?Rmzu@PBV1a3_Or&}iWyKQ5^ma6^5b!EfrAQ=?b) zlohPu?TsEt9_|MDoG(s4=QIso0uP!pmoe$7+en0sD|x~8ZSyvfjre^68DrvIEmdMq zLHKGo?Os}5GqP(=^wsAA7~xL%utn+H?jp~9*+!&CdWnFA#t`q0*^(Y0(V;Km zy9=^_vN1{Aa3>m;atJjsnW1P#=ETS*WtkAxCRz?;tu1Ia1 zYDj!AVo<0&A^hSH`s^q>_=L?#UHnbE zMdf#M%S-P!oE0Z5C$rR2&awNN{r)FRlt$Rf5tpv38v%6!OCEgZ_vwM^y9<%G=Leb^ zd(L6L8M)`&{|#FI31O5I!3$pcQ&&6c59%m=D z&_9&wb6>|u(qd@XH&pi>UXm&yDl&RN^IGR zKw-9}gv3_5Lo2$+!|K8@5n%Ij0C0-T=spx*r`~8!btUQ*UI{l@TbXZ>VSc*%;q1d!4z>yB{&BL z)Qf?LCT{(sS2a(y$B+y%*(bYx`VBtG|KelyRbTu|npn}Gbb5f!X*=ae$fqsuH{K+pzQr*%qC4jj^CBTWIw5Wjj<FoVmo%(&O<&FF_6tqqWY@@v-F>0w4s zyU--AVFXNnE#u{$55F(hP_FwQlkEOo%?%j^=fGWWio)=|Sl&OKqHWQwu_foDY~jcy z6ij#72URb(NyKVbnGQIytW7F^h3&dSrKAss+^TfsMs1WfYR*{Pv!Yz(BuAY4xObc? zCQ>fkMMa!g^Afgaqf5=m5=Lqv5q#xl=)u4!`+l77D<-unj!N^{vA##tLKDr~qSPXN zm#)>Q%~8<3v2WwgV&r!-P7D>O+O2?RWb)eR+p#ai0i~r}I*yiLzlrb3RyGo2n3not z>ULC-l|DA!GW%~XA*r&L$$1mB2=nkKn7ffQ=TZL;3lOm?gMyn*b7J&)o6rsC<8|w_ zS(|}wlTmfeeYVN?HAYvv+r)RNwQpYR&SCcyPpkZI|No}%|Mh<4nnA(7wnGl7SL zi+GOY3@S~Ga&5Gsny2T3H{C1!tlFmSaN{|MPRL_Ui^Y%_7#(F34rv*wDJm{)N9qjC z7%%x`ol;fEWH2*zNKJsvT8HN^Pa3S(8zotIbCZVQht2%&M%Yr! zyT789jlwI@g53f*VHf#pLf3R1l6aNvl!^W#g|bP%D@|{gJAkOG@N2y7z_6*(kF=4$ zEFkc7hzXgXni!z*X3FxIA*lIv>O4$Qif83}Gv$_>)qkDf=3)Gf^A0t1u5P)wIiZ=$ ziV*79`N~aRQ{!r?YdXlzs19~{d9m^D`i%afyUzS zTeQ@M_V-MAziGHS2CAWrf(iMa<*dx8N3|jeqw>1+@tmw9^2Ql{mL4*lxm3Qubl~8rX&H+L{aWilmXaQ2jeKcHo+>eKY>s zGY3Q{@eQ;svNg6uR=Fm{wF-&8h~M>2Cc{5}h6Qx^^r$EVx)Ni-%)@gdp>0hX1^dv_ z7fW1Ivb=&nk@#uDa=gs<2Rp-d=?%JUlFuJ`ZU}sVdPLG1e(=E1MI|*SY=J1^cslZk zgq4|Idlj78BAoQ{P;_Ha^ZtE8rM{wVJ98(7&pW5eBbs$~#6y@%3}kLFuXZ9$){(B- zcH?e2U1trWA0Vy;9>-otFyqCO>h)WwEaNg$f8MdedZ&8Ac>tQV`#NPzhGTyn)OwWZ z$b$5T9BHwP{XNpc&@ytw)5Yc1V>L!vu-@ z5KfA#cw%oOx?qFgU-Z2{MX!!lI$j}N?7w0OykGv%M`cBZvL%PP<-xb+ETTB$wPy1s zEc{2CDZA*fhIgCE)cpsW06}-IDPt3l(6)7HK|JbwyOW!n-2DBo zq>&~66xL8z?>^L7t>~7i1l@93X~i%{iukRXO=tk;&^GV-WU*# z+O;HaOCYsm7-5g?1o;VZjuXG03#a1xJrh`5HjAw*{Y&qmk#M53_NqX2bDT=B5U)7|%TEg#H$B8O;A*jW%$?9^G*z=#5JrSC)a(I7fnSW2hqW;+7gHv2_ zTwy=+z7CHSsL9)PT$TQHjn=?Sz_S5=_`hB_26s5N+bEC7C8!)DqRI9i#?W=as@DA0 zb#K0n?`XMRM@uQJ;y|L>HAQH)76z1y$7J<$jjI5Isj$IYxF^Fv2*I=9M#K>xu6zIU zYslrPepLXi-`ruFpg^Q)42r%2ReqT;1V-;K#+8FwaH*i38l{foL~vrU=dAP+mv}-m zp_Zm?UuRtveTqG?f~ta0*xV9m$jlufzhFeda>!+no%%~W&Dd{`RD#5j9_k0F{9WE( zk~{BlhM#9`$M%;+_a&?R+@t=Kn4d6=9K}w!BpKuYq7q3lxKh zP%dn@h^kO1tQ3+|A228g|GU$ZBoRt^Ukb~cmDh$I2Dc`NvcQNK0}8j8mGe4j+)SiL zcfL=WhI@vDD$ItO6-nV?AgJ1o^b^uO$4H% z=N)HXnd8#dymjz^G<~SM5h7eCd9`9K7H_6n^g+Y&8c?LmmGy-F2=ou^?Cy-y@ z6EA}AjryU6WHY~N(N5aC?7aiY2omyoW0bj$0cyiO@nNNHU|ou9nv`0H6@_`}{^$B& z<`YeR|NGk28ocu0t0a9^lNM#_*7quc>Hi$1yfkr!K*;f}R`^Nw5_s8IfF^Bu$eJ3e z3$SxdQ(b45pRwF#{C1h?F0oxC54UzX`h;Y(Culobe-l$Y~d z2WwZ|xkESW+rr92;Z7iCjN%ZaAs+S2&&QT{nBD$}gW0ud(@6;-n_Tl8^Pt5G^CXRS z`3obYE^h-|5H(VU9}nrwE-6vgPWNI7hPcN0le7$R-4?bjeZhpPaWynz;E;#GB2Ghx zOE>uJTr_p$!*8EZJHZvIiQ>QYfhJ^4li6@@mN~_dkAr>niLzt89*pgN-_PFn{H{4y z=26Ty6W_pxzxHCADEeMFMBz-SM%uA=bEX*kKH4a%9vXPmY37C#O z7q1p8enWR^=N)j8qjIMvXvvOvPcKh?QAfsjAO%7+W`-GtHIaBtu&eBRr@jV8 z|NS+R{Da2gG;BNS@S#HsJAU!S{?fQPRsPJ^0XvjSCG)A-_zaDsQEJ{$ z-tR(L8Prvk!}8L$@8NhA`1%RA|H8;AUZ?0?Q|cYaOvN=|JH!g4jty7i2s)OrZ$8-F zW$!hiV1o`{{SCE&A)Kod-lyM9$Z{%5uwov7i#;MXvkQ-QiunEg~jU2-N1p8f{whUPueq<$;w}T$TT~WF%((LLcZaiPn zWo&a*OL>JLz9AN#c)Qz*tTa2Xjj$21vrYGB-6W*~zD&q3DCvOBqdvVQk$J5FXLa!h zR^GB?_KIi3px>;D`8ijI6Y+Q3!PC{Ss!P0t>XuQWxvrq`2|!~&QI6c%<5OGaToF+N zjP-)kmqsx)WGr~}G6GUJ;Z3;6Y@gX2W&Z?+Yu^&5RsQW+D}?hMJo*Id-;uT}jLxj{ z!70}d%{1>>~HTJ)f9`!EWe-L*arbH3N^9%-SJdc(a3c-5sFZX7F%nl z@#VB%SyQ=gJHHZb+3__JZ_z6l?;KYenBrZ=9}qvI&ing1%=~`MpUr10x%1N~=<&ww z^Ak1$Ml2Ny_;z1Oj!m}NE4{o- zg(?&^{oyK$$5yf)dr_qzX~DF*I@>TT>pB6CAgx<+pe0x-@Wl`F1Ip-d5Gn0L{RKbHg~(hfrXLBcLUt+ldQ zr$I}$df?f3v}V`^Gwgjn>BO)d+3YoGzj|*C$Qu`4ubC+KdHxd?bU;NR`*Tu{B@Cg5 zLO%t1=oAs(6WhZtqQt;S+!l(IsK40urveFj;}4eIIk)FXIUVl@-oHIzXs;@XI+FnV z7fz&U5AF}{FW^u9e}%neR9s!NHHy0>xDyiGCAbC+F2RBa2*H9o4UGf~5`qSIcXyg5 zI0O&wZjHNroA;dWo^zjP+;QO-BO}mj?^Ua6&6;x-yY|lsRg|H0{%J&5eV^OS0-8(- zp5%pGss?>jq9f@)iVK?I=NkPpK6L9jRX{N|h#XXBe#{WM&~8z0F(pf6y05eGcMUfz zVU*~U1^1%j24C#|MfT?87A38C-+92dmXox;*-EoVguIj!LV<9a!(!7oEL(Ii!z-rt z){Zi1lP@I={|%`s+Qf(XUQ3ebQ2NU>CFNgb>Uy~K%~F~;8(Qxi7E$uzWEEg%;F$U| zqz{y~X5aZWiIlA(zV6Yn(mqiPbrRfcO79GHyNl(KRv9Otl{Z*_qvj%uARz0R8sAx* ztegzU&fA&XVf-ochw(wbl7esVPFYUs41{M-oV|+ zsM{5yTM^U8f-AWDr{3t?I{9&N&msBHoMgYG>gJ`_cyz%hi<ZAK5X+Yw~AaMqY+ zeAg#!vl%EMG?x@a)7&WrvVTv2K5wE1z}rk37#IQn( zidXBBxi16%LRtcnu#V-$bl+lqEkb;c4ZeblANN2|n=513;C}eEg7n5zmG2&ZjBP{k!-D}If{h*gUuohg}TtH2>rh`Z`ikwBI`cl+uQbFjwI<4Mh6f*PfNq1PajPs4OKDvS>V&P>% zj(mRFZ9jMkZL+pYwhX@v?DW8bWK~_g_(Kth0*z(Een*B^*t)4B^xoGl0z!N31=XHUMFJ>#7plZ; zEg9XqxNyp9vF4_&9=*R;r|YNoBwf}O|9~P%u_y6$Z}v*M7w_If-Q5Xio8d9-clIjjsHhBLZct+Wavg zuoZHfTdfAY*YV94j_kxJWAfAry;90dP`2!{VPH2P{%Skk-=9o419JLayka28KMf*A zJ*$nCV)?Eh8Yt!4DVxzk{#F(RcG8;huY=7h4-Vi}C*$x112x`HHJT$tcj{ZT6{9=_ zLK0zLk)mhw03m_5Sj^pt$}*E~63id5Iwk#sjz3a;vC$&k=xz{E>0huBY<4F%i6_L3 zj!o2P^q6a=^-|wYF1!Uxt+d)9-R2{^S}dyKsh@te$rscPa+rtTOcz$i*7BoF%6>Vlr?AV-Rny<3 ziSTltmUPccHnbvaZ4giJ>-ouP{JwO;_hq_7pj-F9`o_<7zF8KqM!=WhsuS4UO+^yE z2(WhrvcLes4u8Z~!ouWpi|*pK3zd@aD(|bfIW~B^1UX;eLUTptRQ|gDBLAiNo5ZA| zw0hCCBEmEdRSeE8(yz0PiY!bO*yg*3AUix0&dPPO5cQs4nsg=|_Ojd&* z6xOWz*)1Qld9scuk(Hb=Jz%6Qh)=l@VEdLRq7Req8U^*8{bov15wx9j!w}KSAt%Xy zCTzRsgw3bf8T_2Ee`0KaR;VbhZ-@=ha9uq}0O=euCAAM)z1?y;g>&sULwtx!Y>s>k z^wjg0Q3!_2M7E@I<%fY@Wxrl&!+teJI=S#7MAC4X*_AkD_RQIr)odxb(W=u!D4oi7 zGvMaFX*(V{1+BH*nbGW-BE$aC%EhqUEya}AiyU=+g2~ZzfCL z?k;h~xONXKsr}_0GyNnh5dKIQ#Hn4wc0l-J@>=G$VSj|=(qwZqf?#dOy*J^pX$N9?v{_Ygy2_ZUWQRb{2T<8hVT7ERgy6BYj2sGQTF z{YW$j%ceuLh4-7R@F|6ecm1#48z%HcDPd`UsOAwXkd7nb_8_SJ$R{r?oic~+osTwu@OD=wDmTB@ z8816PA{l{WtlWV8&;M)95R6=Z1gwP#lb16{z@F%fLAb$gBxcpcgEencHLmTI05!^T z*AI9^)2BzokL;i1s)IZ~A&d4EW)k-*petOASIAiORqmWyX5} zOF&y6$_hxyEW-4ZF~*?aeW%TDp2Aq8|B+rbA5Ozrt*94@AYp(7jUzYu?2geI4)z*1 zv)9!y^7cnAvME}Wz_8U07^GJ=k^e%7r0$q)PMC*i z+U%b@W%Plu&JNLKfCqk6YKs?P205T5a`m@{c`FlC>+2iY+D7q4PN8zLmpv>wa0XE4 z_?egQTgE?^1vvIrvmiSs*%Yhi5an5f#PbJ1Z}j#R zZu|txkT5TE#5BV@;wh`HqMW+z;g;P8VzEy3{RQ25q;ooF(#C>M>W<|9bHx4Uz=LpU z1HA(-qSO|ks`rm$`LPW4?pBC5SqM5&%anj8d21^$lq9w|r9>mAFJGy;qcO1OvUNZg z;5KynCp2zwR={PBcR==ojK$&(%}ob4ka$sn>vzQGz_qdAX~Se+*7A-AgYb&B8a z=#n(tVk!T*JYuSX>`T3m{P8vN9WOCwLU^S7_MqVQ(r7DNreC^sF*GD~W_kssJaE0O zarpPwuP5EfC-YWo&Bzss`<;DCauoN@s0?2xDg=eaMSxG&T<8^LcdK>2;r6wAZz8&N zDMHD#s8!o%z>9Ap&wu`1rZaN%QRQ-o<|j4yr?5?);d#eS*jBPV@7Rdv9s6`sTC)-( z*YcCVec6-YV5WT{o{nm%ic+H3d0Y46-~lh5(4iQ?gVK50Yq#ZTP`Hzc}AO-Jcr z!{5#mc&!KG!68|{{6BH~srC>Y%5ACz?Cj-xCfd-|_1P^ZlkprHnSu-a)*x7 zVwTnPJ6Pbu-Bs5i|6(a!H6B-nTV<|X$6ZMJE!({x6Cdd=9S^SI6EDDC-%L!t;XqwCH*8nG8i~l`JPK?85O(Bv%<4p)QODC;~ytMc{59S)K zcuWf{;sBlJBWEvb)>U=6PBU2eq0P5(VF~f6MSF4lN=Ruzij%v|`n~4b4G~o^_5D|# z&p;>wc;@zvbMF}>PuA#6F7~u4Z3;pMlsX)@>tz5%R*(imDjYEFb68!cTQLrtBNqi} z2i+kY5)2On3}9z>UL3UrgLv!&VPR zfg&$g0f!Zu())*oxOzLDm)r0aqAl*;G8%7t9fO#(O97yIiS43dx@#M7y7w>Bum5eu zZ5-ck4|%Acw-4F3Z%6-IVTgJN>sUrxNJEdn9rD?MP4lkS#0kohq$Up0x45+LkiDW^ zr*z}U>#nL2F5QObBc1#f!3^PJD`t$jpjTt;)c3MQ$g21D%E<3q-1ywHt1kUmZ1g%E zO||+aFls@0V5^v;o7Brh!Qi`#we>kAFue@xB>wSYk;Zsg;B&DB6wWQXOlAl!a^Bo~ z64W0>p;? ztuL5oS}bwR2^irR(p+8=uqL6GXg{jBDihCX0ajJTfn)2?bbN)xV5txs$!{18P(XXh zxObCF_BPYRKcLcUO2Jwc?g}2{B^mj#u8)c$rII=)hef9xt9zjWz7p4Qm4sQR41s*Q zB7~))9p!I@@ix5s3D&&OQ2-`-W5?a4zFLW!-AuCu%fU=l+WE(huapl>Hq-2R#*?|9 z%jO)eM6rUVhyCXBpb&Fm`8cG}Llzt!u z38N7EUL}@ct+Pf`N}DwzgD(FsfHuh z+pfn4S8gmbUfppvKZTc+2Mb;7b>hDco=AlIB5oa+d^FQ;ctSc!v5nOsBB3nvq-zOG zn{$AoGr;-2t#kv%$F#Ut@Ka-}iW1I=jN(*RheOQ}Z@Z2WF`A(Xx0>w=X}aGA2# zb*R2sV6l*x$Ljh|Vn0YtI!+U@8u=V3uBYAs9JfptE9%Den%LrLFzD{8b8f1n$dIye zT~;#Qdg_bq>D>EhepTzTK2-?i^zduL7T~|Inchwtcj3R5?ZH!|Q{vX0)@1M7wNQdH zI%4JLG@;2hM9Jnd`Q_7BM2uE)BI-(^K_Ge}$WY)zQxZlv(~Qzn$<|};7-N5wi#wU){bIw#!`rjeI35brtJ9_>DIRFz10OW9l#tef7c9(+%ULwSI#n@Jn9V%SC2U?U)1~wN=J+ppMHiFbExy4+;(n&X>N~YUhZlX!9|?>+ zV0zalA#`fxKAs-Ot5N5yHEO21muITSdxIQZOKsjJ=M(Ei6^6oY5a$sQ#O+i+s#Nbs zWF&?cJMCAODV1NwXs_qRVF7?%6!-J+J<*cq*>dIS)iQ7ps;IR?!;pq?VavFx(d3xq zb!fZAQ^hfD*VL0=j!rEG``DE%#`6yYEA93_Gpc95$vaL0#&|k7EqWp8Z4aj-+dtf4 z2V{UyvFTG3<5*?LQK?(6aE8(aV;|RRhY{_vw1E&fUXc`VFcELPzCWw{e8D_kwAe*y za#g-~zC4P|&An2>A;z#0b$qd^l7^m1H7thV?RMm!k@zSNX*+^Q66{7Ds(QeTkIzQL zUu3cw=JP7~TF}b0 zR9R)*?B+#DhCEz{i9`cJy! zjH~4{*lQYUp-*p=JocCn4j)3W{HYV~;Wh^n18D(u=H-z6>Vx|dJq>8^&|RWtYuM@x z#V_N0Kw9^4kYlt+QHRl}`i6Sc!4ZPqsxoDA7b2VraX4+8VVUK&JqknwL2Q3Gx)KOE#863qVTC}jWV-zb)UUg1k2TslDJ zdbr2r2;zD4A;O{*38kTt#mi9JLn0f5iMB9}H|kf29d}Qvn*KS^Q>${a2=F(Pje=9U z8Q*%W|3D1SDa)!;s2gK)5J)McQT&6ypcMrG0Rr4`#g*`ga>R`NwnaT`P5iEX$7uJq zM=aB@2NO90r%JRt^|n(nEz@0zB#n{wqeX5R&v(XuWrtL>Uw#CJR1nE5y*9A}#l|LV z*6A2u)b`$8Ex3!{x3B43`|N#y+%J-pJ15qhZMpsUVNE7(kRt26a8)YHx-NF*l%*o5DkmdTInbuu1{)~3 z==$b!RK>6N&PGF}whd98bRTYm!JW8uO>Mnj$29RBTFF z2&e}*v-JVsGg@R4N4wkreOSLfNRgxCWiZwbYNC-Z@?@C~F1^TG9Bv*S^f$mPiKz5C ztNADiF@#0HEOMM;IPI+>d~MPa3P;ReBz_d%+Y`B@+hx_2Z#wjyD~}-4o9aymuhkR}w z3eDuM_na5ZuYafD2mOe?JixYZ7yEV|R}n(Ji}js|J*aybgsLijM-f3LhV>bA$2M80 z7szMZBPvK%9sLofD~Un&W#4kEDPk*NVWI|1>DigfVQ9Us*hcR$ z>jTIvXIMQ{c$mz7R4jfpL*MqLpl0_N}kh76Td?7cOQi{k?+XGJLm5O0A5!ui+>SEcRrsrY`({#y3AAarcaL5d`(3GeHBs-|@5~-wB7%X2 z3vd*}cgaNs;In82{Wl!*m+R+;-zB$;4_T%)Jtt0@-wSDG8*zTH7{C5~C^9`bo89VM zWflz%2D_Ol;A0|Po`#{NT&N14hdNFC0gC{-)x->av`X4v?Ok|z1!DEr5aHs@R5AV{ zVq!PB%d$XMbxCiozM^frJ@4>d#jG0Kqf>S!vi)a*7@n{g!e z)}2oZFHT2@jRTr^EWKiSlI9R|Jh`B^H;Kmne{dT@0FwuejZd@Hn=7i3I&en(4i4szF^Y03um2Wtkc5WfXy+}-&sXPxWw*l0A_b=SQNvd*b#LsYuZ@g7*cv#MyDz&u-3#eXB~-2cdBz6d1OOAaZ&N zr~hsH#_8ays-HE|dQU{~wox(~ZUYFe-rijUeN`4NN)H8w25q^3A=((9%dpJRnuIGpOJx!W4r{$OiD05o5=&%#IFgt|4_{>?2R=9IC0^W$GJ+&29 zujPj3b&P%B&$foLu)qK1Uyx6cB1Oi~;Jw#0>xCS#Xa^x&wahK-Z))^bh_^p%k&kN+ z^#X1cXsPRo#Et#|I>6Ya)jusq!NAa&aTJA-a>G_B2m9s=?YZBcS7rQJN03eeE{=A3 z!94ZX3fn;`1b_q3`h*H4378FQGZE!<5W4NK`tlxUJf8{GV{&xHn#P*_G!TMDCBtJsa z0^YT+qU+X#`-QknvlXqmIQrR(SlnMDFF5U_e$Q%zYSO>Zd09!huX}8#OXXO>GK`oy z!v02zH@gIKLt;8PVEL;4wBAw9(=twuA=}bJZ=Oh6!PHB|@b^kG{V1%1+BtXR3t)_d z2w~~xn<$oXE*1h7k4@7+kR7`xh=aH!ygtU!W6?Aqd8Yn@co} z52KRj1>u*mYQoLrUTic+)9}PP8*R;wXk0RR!G@FOv+|bR#{kbW##<0@$!4rJ66yM4 zCkmykYuf%JTo?h@iWb2&=Y3mSVLGQ{JkAqTtz+Pf4e#E{x$M<;i=An8wpD4?i{o+I z&!r&Ei(oL+`M9UZq3#(0OeOT-@;{-K34a$DjGZYN80^raXW2!rN#8Z1=%)epZbQLd zF@CydnCdn-h$Q0=uF8??>M-&|7ODk-HHubTr`xWe#mhR(5X0QKPS1B4c*MS-(UvE; zq-!W!$j#2wP`PU{w3}R-aqmDbAb4t!onukAWYcz9Mx(#mF0C*`S$V=$lI$fquoLv7! z61Du(2T|Fr?Ov7!@ZHJvU4h1?0;gN+8M4Zg80gDuCapv(5%t~AN`lMZMDqAL#KglyY?azCOd>(2?Ti2Yf-M7hc75 z-F&^9Zsm^U-v!U5tn7SV#hx=v5vlVsB2Wzt;zvIFy}hj{xtZeHR}pV5e@?gcavbso zXa|*L#K~mJoiKxnH+Snn@+%6AmtsLn*Ho@c!Z;lXfBY|cSns58dOhT1XNz5n2?a1p zeu4%mqdWS3*1c5`rBbDc53u7$BVqg&V^OJm8c?7&N;Gq>Fe|Dnh}4(4)%gtE^Y@owb?2~ z9go}xsY!m>8Doc&ZccLvKLl7>i)+ZzB6Xe%XHrP3d67ok0FrYAzttSpCH0J|TNvgY zRNgrPskhL)wQ?A6GgVK&Y=nTWceqA#Y%HrA01wyEGIwJSpB1=5y%wU#&9K!jG%Pcm zGvFUO18&YTN5GQDkKRX+AyOXsg3hksE(YpH)b__yMZh` zK;+O{OP6TiCYZWZO6sq3aFvBa36J~N$94ryU_aG)1#lsvPm11^<9rx^h=F!65gZNY zo8Q8FMkMrxp7}@PetT-YqRP8z+8I{_Y>zaV>^=9%?Q`S!ua%-4_~G3bba$sJI`DTN zb_-l~td~2bR@xetG{C}hV5@+y?T2p3Ity<)J)7$moICho` zh;Nhu4`Nfuk5`;t8}b1}B)j67s`j@(5w;OKf*=c*lr19GNHCk}@^4gacN*a|sZy;D znrrs%i^?yKHU|j#2oXdm|y(c9(V-N%9{Heqn?;9t&L%pv?+wpg8WXs2igEh{Wjhu%-+1CHV+^%SWm za5P_(A2vtaOSZm3Y$O__C%*S-qxcKmNY;x`X&SVB=_=y5OL>ntE7v_zO?^Ff!HlKz zI(JHgeo5gf4qqF2W&}KYwk3#qL>9&4x>9;)7wYW>X8f+qe5@_q*a9&ulv?jU<}ant z_o>{k5NzN`y!Kj1|ATfpLOVKLN79D4z>|y3GVx+CN=wq9;*ZMA-)~9O@P!t4n!DS7 z{d#*JBxuZkoTi5^L|?@Ov=q=T$R1Q}xcJCnLf-Q}ZL&;v;zeo;+Wpx7r=Y$RQl(C# z1)*HsdcYNvKPGX`BWBY;TJk-^zTU`*{z3!HTFhFFIyw1i|@U+e);uG?OR?#UVfQ`A$|_+{s% zTAQ}Ag5~MNI5qkookE9fXRu`zD~Yf*!TsethRkuSO0f#+$F*+`ymBoc0t-|Yjh6CLWg&}}1cj2;~ ze#0~Rxik7fq>}J;y}A?y3Di-^?j-}1GK+mL078Zv(=ES!bGG&JnDTK3Z@}MgKFwVt z<+>Xv+yRRwoiQZMEt*Er)gXM6;k~zj(O2w>xNM`@T-<{vc71m19^>sY4bhB zN>uwqxfE(Lz6goS{$I9Qc-n$ENvY#~gxa$NVHh|N6<0FsWjE8vjd+G{^4`kUKwp&^ z(;gKFVLs)yzjsN16W|qMt&pfP8lo6woHGyIUy=hCn#324Iffp6d!w&>n_mUp1J@db zWc$U|w{j3l{t$-=MAt4U2d-_=TYjp?k4;X5bBs_%xAP}p-H=!}i{mJq7^ zCP{D3yfGeCapZ59gqPsEUFGEQ=@-|oXi;WB7o@g72zC0C%2T^&^JdAHs|5sD?-pC> zrPI>Q_E_g1k44Y@!%E8$7(vP_r@C!U*mw;y+4w-rF%uOQe*vQj!krg)6lecAnmsG| z76W&26c1s3-;Kb9Q;w3*>`U(UN~w=BZ$Z6aSix7H`U(>#Gi{Bf&&N6F1(@@w7_qRnVj$W!wzfP4VX<1Z0 zRB!k39~u$eA>Rme85#uGuaTy23VX)?4OX+@4_{&lhS+i9N}QnIgU zll7EpcIZyzKEKCh0-I*=Z4aX(YanqHvYlK@{nvREz}`J~;Q$qf3Q4Ic@FR^pp?dcW zzpP%4b`pk61o1qS@rZeum>m-FMK?APFHDSWDphy>9CBLBM}j!&XN`+y5!_6>`A zYvVQpfWzcUspC?LVUhnWyV9Tz^K}#(jIV$rWR43lY;o^OP)_ORcSu(e$RePWP0c6= z?#lMM6WJU{%WVNZ~ynEq1+|b6ajl=`nG!P)#{@{NA>lIV_vkzb-HO+^QlL z?u=rI((G7h#_aM7O+!TdKAkmNq!o7HmrPmvoXdCW!Kv}0VmoR58%37I$bH#J{W`WO zGJpXYt^tmVdbU#y7`FVIR zh%6S@O1Y4h_|F-}2E)oyImo;5cFyVJe}q_B9Vyl=iq`MxwIA~UH}km4?v z8~6EAGx<{X83;ml;e!n6Ct2+{ead57tFX*%KKGC3^Hn^G_>R1qeT@s6 zn+;>ek{d7N#C<>aMD)=kT4=Dglrkyl$MMVd>j&H1{di|g>*bffU2j3>S_XD>T(0G( zqa8Oa6QQ2u%`8T5&JA1KuI4%5P&<|{i7e4!JssjO&VxDh{eh(pQaYdggQ)@Z@DZLH z`VzlICXt{AfV*LE05T>dTG1DrC;lj5j7u?si7RHeR85yx-C4^II5_7Ae!_3#+-n)z zE$HH($fx@lKe+9Pe2VGNd*ztnkC^GW-UAL%5T9#Or20jw8CRPu_mN6Im4L7wQLl@p zja~bL_?Tz8@-Nuo`pbvG6lCA8-p*79g-MQ0%G8CzwPUS=8Nfg8^MfU;HOCACZV8z?5V=5I_Y~+68b2}1_y_@@?Ki@HgM$bL=Mu}W ziT}_(0TiknfKEEO(^m!%4k0I+&mUbx9Paf(iB1L1)5A5s$JTB^bVlI<56h457&F|t zeZQ2I7DMPd`k8o#tscxb!(&H#34W(5`PFj;M~d!|&Y5BwBxay{UqmLF_rzrEo12?A zEZ4f-=*AkkRQTixRY;;Nq-MyhRQ*eoVE8Ob0HMtKKGXd;AZ?%W6gW+eX&80dMOshh z2$Hxxxm5vi9|vG&*O*3bx21T3PeHJsoM|G+l#Z>*S7Ge-9q4$L_ygS8QY3J!(>U?8 z#lL!{3(wSWnAITL^P2*yp$@n!01<_PUj=5#$It>TQ%jS*f($Bh_$K22Itl#O%OJ(W z`w3dpk(K$oOvXhp_a8Bf*B<)A5C1B!B-DT<91uP;T&DlCgjJ>jHJmFcR9tA`i`OQP z3!a`J>zU{EJrj8uk*j%#?2MiUt@1x*d7iL*D4%B`uSS)v8~G={A6|Q9=2eW9Y8bWOVe0V_+`eJ9uUmzX&xI1n< z+}FORve0E+VJ8!;`dHQ@7fsNL3HJvTJds1Tz5S0R0k1(Api7QW8a6#2>jG{V8^kM1 zTYy(=HA^{ob1*?_S|u$i4ZMK=Ukj4>%2IjkM{z%hfE?Xg>39(?<0!0%);JicT|Ak& z^+LeO*G-m>KBR)_p~7Y1PZPzzDwj6}KrC&ztbcso0bzvc%#!)3yt0ONAw4uT^(p9k>}q7S#Ry|V>yz}I^j73uPK HCISBk5Y^~l literal 124069 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%0AjSE!?aoK0<4X2`4{-@<5*ZpHqf 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 k5uwSWIp&y=1pK(i?GMpcH}~`0?a>FNHW!d7 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 From 0eb694f1d76357163b2d15dcdd6d8fddd2bb940a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 13:00:10 -0300 Subject: [PATCH 15/38] refactor: Reorganize imports and enhance type checks in PythonCodeStructuredTool.py (#3266) refactor: Reorganize imports and enhance type checks in PythonCodeStructuredTool.py for better clarity and robustness --- .../tools/PythonCodeStructuredTool.py | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py b/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py index 0a0e35ae3289..36e901ceaff0 100644 --- a/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py +++ b/src/backend/base/langflow/components/tools/PythonCodeStructuredTool.py @@ -3,17 +3,16 @@ from typing import Any from langchain.agents import Tool -from langflow.base.langchain_utilities.model import LCToolComponent -from langflow.inputs.inputs import MultilineInput, MessageTextInput, BoolInput, DropdownInput, HandleInput, FieldTypes from langchain_core.tools import StructuredTool -from langflow.io import Output - -from langflow.schema.dotdict import dotdict -from langflow.schema import Data - from pydantic.v1 import Field, create_model from pydantic.v1.fields import Undefined +from langflow.base.langchain_utilities.model import LCToolComponent +from langflow.inputs.inputs import BoolInput, DropdownInput, FieldTypes, HandleInput, MessageTextInput, MultilineInput +from langflow.io import Output +from langflow.schema import Data +from langflow.schema.dotdict import dotdict + class PythonCodeStructuredTool(LCToolComponent): DEFAULT_KEYS = [ @@ -260,10 +259,22 @@ def _parse_code(self, code: str) -> tuple[list[dict], list[dict]]: for default in node.args.defaults: if ( - arg.lineno > default.lineno - or arg.col_offset > default.col_offset - or arg.end_lineno < default.end_lineno - or arg.end_col_offset < default.end_col_offset + (arg.lineno is not None and default.lineno is not None and arg.lineno > default.lineno) + or ( + arg.col_offset is not None + and default.col_offset is not None + and arg.col_offset > default.col_offset + ) + or ( + arg.end_lineno is not None + and default.end_lineno is not None + and arg.end_lineno < default.end_lineno + ) + or ( + arg.end_col_offset is not None + and default.end_col_offset is not None + and arg.end_col_offset < default.end_col_offset + ) ): continue @@ -277,9 +288,9 @@ def _parse_code(self, code: str) -> tuple[list[dict], list[dict]]: annotation_line = annotation_line[: arg.annotation.end_col_offset] annotation_line = annotation_line[arg.annotation.col_offset :] func_arg["annotation"] = annotation_line - if func_arg["annotation"].count("=") > 0: + if isinstance(func_arg["annotation"], str) and func_arg["annotation"].count("=") > 0: func_arg["annotation"] = "=".join(func_arg["annotation"].split("=")[:-1]).strip() - + if isinstance(func["args"], list): func["args"].append(func_arg) functions.append(func) @@ -295,7 +306,7 @@ def _find_imports(self, code: str) -> dotdict: imports.append(alias.name) elif isinstance(node, ast.ImportFrom): from_imports.append(node) - return {"imports": imports, "from_imports": from_imports} + return dotdict({"imports": imports, "from_imports": from_imports}) def _get_value(self, value: Any, annotation: Any) -> Any: return value if isinstance(value, annotation) else value["value"] From 9da2c34551c9b0bf3f5b8be7df347cc6c1e17d3e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 13:01:25 -0300 Subject: [PATCH 16/38] fix: update type to silence warning and memory chatbot json (#3265) * fix: update value type in DefaultPromptField from str to Any for increased flexibility in input handling * fix: replace _instantiate_input with instantiate_input for consistency in input instantiation across the codebase * chore: update test durations * update memory chatbot --- .../starter_projects/Memory Chatbot.json | 2 +- src/backend/base/langflow/inputs/inputs.py | 4 +- .../base/langflow/template/template/base.py | 4 +- src/backend/tests/.test_durations | 710 +++++++++--------- src/backend/tests/unit/inputs/test_inputs.py | 10 +- 5 files changed, 369 insertions(+), 361 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json index 10f75520adee..05b1bf5768ea 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json @@ -1039,7 +1039,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import get_messages, LCBuiltinChatMemory\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import BaseChatMemory\nfrom langchain.memory import ConversationBufferMemory\n\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n\n inputs = [\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"BaseChatMessageHistory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n ),\n IntInput(\n name=\"n_messages\",\n display_name=\"Number of Messages\",\n value=100,\n info=\"Number of messages to retrieve.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"order\",\n display_name=\"Order\",\n options=[\"Ascending\", \"Descending\"],\n value=\"Ascending\",\n info=\"Order of the messages.\",\n advanced=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Messages (Data)\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Messages (Text)\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"Memory\", name=\"lc_memory\", method=\"build_lc_memory\"),\n ]\n\n def retrieve_messages(self) -> Data:\n sender = self.sender\n sender_name = self.sender_name\n session_id = self.session_id\n n_messages = self.n_messages\n order = \"DESC\" if self.order == \"Descending\" else \"ASC\"\n\n if sender == \"Machine and User\":\n sender = None\n\n if self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = self.memory.messages\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\n stored = stored[::-1]\n if n_messages:\n stored = stored[:n_messages]\n stored = [Message.from_lc_message(m) for m in stored]\n if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n self.status = stored\n return stored\n\n def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n def build_lc_memory(self) -> BaseChatMemory:\n if self.memory:\n chat_memory = self.memory\n else:\n chat_memory = LCBuiltinChatMemory(flow_id=self.graph.flow_id, session_id=self.session_id)\n return ConversationBufferMemory(chat_memory=chat_memory)\n" + "value": "from langchain.memory import ConversationBufferMemory\n\nfrom langflow.custom import Component\nfrom langflow.field_typing import BaseChatMemory\nfrom langflow.helpers.data import data_to_text\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import LCBuiltinChatMemory, get_messages\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n\n inputs = [\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"BaseChatMessageHistory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n ),\n IntInput(\n name=\"n_messages\",\n display_name=\"Number of Messages\",\n value=100,\n info=\"Number of messages to retrieve.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"order\",\n display_name=\"Order\",\n options=[\"Ascending\", \"Descending\"],\n value=\"Ascending\",\n info=\"Order of the messages.\",\n advanced=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Messages (Data)\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Messages (Text)\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"Memory\", name=\"lc_memory\", method=\"build_lc_memory\"),\n ]\n\n def retrieve_messages(self) -> Data:\n sender = self.sender\n sender_name = self.sender_name\n session_id = self.session_id\n n_messages = self.n_messages\n order = \"DESC\" if self.order == \"Descending\" else \"ASC\"\n\n if sender == \"Machine and User\":\n sender = None\n\n if self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = self.memory.messages\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\n stored = stored[::-1]\n if n_messages:\n stored = stored[:n_messages]\n stored = [Message.from_lc_message(m) for m in stored]\n if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n self.status = stored\n return stored\n\n def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n def build_lc_memory(self) -> BaseChatMemory:\n if self.memory:\n chat_memory = self.memory\n else:\n chat_memory = LCBuiltinChatMemory(flow_id=self.flow_id, session_id=self.session_id)\n return ConversationBufferMemory(chat_memory=chat_memory)\n" }, "memory": { "advanced": false, diff --git a/src/backend/base/langflow/inputs/inputs.py b/src/backend/base/langflow/inputs/inputs.py index 366a9d1831c3..d2c4f848de99 100644 --- a/src/backend/base/langflow/inputs/inputs.py +++ b/src/backend/base/langflow/inputs/inputs.py @@ -471,7 +471,7 @@ class DefaultPromptField(Input): advanced: bool = False multiline: bool = True input_types: list[str] = DEFAULT_PROMPT_INTUT_TYPES - value: str = "" # Set the value to empty string + value: Any = "" # Set the value to empty string InputTypes = Union[ @@ -500,7 +500,7 @@ class DefaultPromptField(Input): InputTypesMap: dict[str, type[InputTypes]] = {t.__name__: t for t in get_args(InputTypes)} -def _instantiate_input(input_type: str, data: dict) -> InputTypes: +def instantiate_input(input_type: str, data: dict) -> InputTypes: input_type_class = InputTypesMap.get(input_type) if "type" in data: # Replate with field_type diff --git a/src/backend/base/langflow/template/template/base.py b/src/backend/base/langflow/template/template/base.py index 97142cadb28e..a76e2d6de9b0 100644 --- a/src/backend/base/langflow/template/template/base.py +++ b/src/backend/base/langflow/template/template/base.py @@ -2,7 +2,7 @@ from pydantic import BaseModel, Field, model_serializer -from langflow.inputs.inputs import InputTypes, _instantiate_input +from langflow.inputs.inputs import InputTypes, instantiate_input from langflow.template.field.base import Input from langflow.utils.constants import DIRECT_TYPES @@ -48,7 +48,7 @@ def from_dict(cls, data: dict) -> "Template": input_type = value.pop("_input_type", None) if input_type: try: - _input = _instantiate_input(input_type, value) + _input = instantiate_input(input_type, value) except Exception as e: raise ValueError(f"Error instantiating input {input_type}: {e}") else: diff --git a/src/backend/tests/.test_durations b/src/backend/tests/.test_durations index 1c8db054a1f5..fb51e5b430f0 100644 --- a/src/backend/tests/.test_durations +++ b/src/backend/tests/.test_durations @@ -1,354 +1,362 @@ { - "src/backend/tests/test_endpoints.py::test_build_vertex_invalid_flow_id": 3.1494096249807626, - "src/backend/tests/test_endpoints.py::test_build_vertex_invalid_vertex_id": 3.0606157919974066, - "src/backend/tests/test_endpoints.py::test_get_all": 10.10167008501594, - "src/backend/tests/test_endpoints.py::test_get_vertices": 4.5017141660209745, - "src/backend/tests/test_endpoints.py::test_get_vertices_flow_not_found": 3.7886676250200253, - "src/backend/tests/test_endpoints.py::test_invalid_flow_id": 4.073716707964195, - "src/backend/tests/test_endpoints.py::test_invalid_prompt": 2.7002592499775346, - "src/backend/tests/test_endpoints.py::test_invalid_run_with_input_type_chat": 2.987766916019609, - "src/backend/tests/test_endpoints.py::test_post_validate_code": 3.0467621669813525, - "src/backend/tests/test_endpoints.py::test_successful_run_with_input_type_any": 14.8548604179814, - "src/backend/tests/test_endpoints.py::test_successful_run_with_input_type_chat": 6.242352208995726, - "src/backend/tests/test_endpoints.py::test_successful_run_with_input_type_text": 5.7594154170074034, - "src/backend/tests/test_endpoints.py::test_successful_run_with_output_type_any": 7.347130999987712, - "src/backend/tests/test_endpoints.py::test_successful_run_with_output_type_debug": 6.291947416990297, - "src/backend/tests/test_endpoints.py::test_successful_run_with_output_type_text": 14.872085083043203, - "src/backend/tests/test_endpoints.py::test_valid_prompt": 2.7850471249839757, - "src/backend/tests/test_endpoints.py::test_various_prompts[The weather is {weather} today.-expected_input_variables1]": 2.535564499994507, - "src/backend/tests/test_endpoints.py::test_various_prompts[This prompt has no variables.-expected_input_variables2]": 9.15231529099401, - "src/backend/tests/test_endpoints.py::test_various_prompts[{a}, {b}, and {c} are variables.-expected_input_variables3]": 2.640623040992068, - "src/backend/tests/test_endpoints.py::test_various_prompts[{color} is my favorite color.-expected_input_variables0]": 2.079908042011084, - "src/backend/tests/test_messages_endpoints.py::test_delete_messages": 2.515260499989381, - "src/backend/tests/test_messages_endpoints.py::test_delete_messages_session": 2.3651068749895785, - "src/backend/tests/test_messages_endpoints.py::test_update_message": 2.5627032090269495, - "src/backend/tests/test_messages_endpoints.py::test_update_message_not_found": 3.504595792008331, - "src/backend/tests/test_schema.py::TestInput::test_field_type_str": 0.0005162910092622042, - "src/backend/tests/test_schema.py::TestInput::test_field_type_type": 0.0002682080084923655, - "src/backend/tests/test_schema.py::TestInput::test_input_to_dict": 0.0003857500269077718, - "src/backend/tests/test_schema.py::TestInput::test_invalid_field_type": 0.00031291699269786477, - "src/backend/tests/test_schema.py::TestInput::test_post_process_type_function": 0.0005505419976543635, - "src/backend/tests/test_schema.py::TestInput::test_serialize_field_type": 0.0002683750062715262, - "src/backend/tests/test_schema.py::TestInput::test_validate_type_class": 0.0003414590028114617, - "src/backend/tests/test_schema.py::TestInput::test_validate_type_string": 0.0002427089784760028, - "src/backend/tests/test_schema.py::TestOutput::test_output_add_types": 0.000245749979512766, - "src/backend/tests/test_schema.py::TestOutput::test_output_default": 0.00026183397858403623, - "src/backend/tests/test_schema.py::TestOutput::test_output_set_selected": 0.0003107920056208968, - "src/backend/tests/test_schema.py::TestOutput::test_output_to_dict": 0.0004964589898008853, - "src/backend/tests/test_schema.py::TestOutput::test_output_validate_display_name": 0.0005334159650374204, - "src/backend/tests/test_schema.py::TestOutput::test_output_validate_model": 0.00029370796983130276, - "src/backend/tests/test_schema.py::TestPostProcessType::test_custom_type": 0.001362041017273441, - "src/backend/tests/test_schema.py::TestPostProcessType::test_int_type": 0.00023837501066736877, - "src/backend/tests/test_schema.py::TestPostProcessType::test_list_custom_type": 0.004543458024272695, - "src/backend/tests/test_schema.py::TestPostProcessType::test_list_int_type": 0.0002362079976592213, - "src/backend/tests/test_schema.py::TestPostProcessType::test_union_custom_type": 0.0005842499958816916, - "src/backend/tests/test_schema.py::TestPostProcessType::test_union_type": 0.003973040962591767, - "src/backend/tests/test_user.py::test_add_user": 3.298028166987933, - "src/backend/tests/test_user.py::test_data_consistency_after_delete": 10.030325876054121, - "src/backend/tests/test_user.py::test_data_consistency_after_update": 2.9754588740179315, - "src/backend/tests/test_user.py::test_deactivated_user_cannot_access": 3.544328290998237, - "src/backend/tests/test_user.py::test_deactivated_user_cannot_login": 3.9071091239748057, - "src/backend/tests/test_user.py::test_delete_user": 4.161238500004401, - "src/backend/tests/test_user.py::test_delete_user_wrong_id": 2.7632550839625765, - "src/backend/tests/test_user.py::test_inactive_user": 3.334006417018827, - "src/backend/tests/test_user.py::test_normal_user_cant_delete_user": 2.9729639159922954, - "src/backend/tests/test_user.py::test_normal_user_cant_read_all_users": 2.6966073329967912, - "src/backend/tests/test_user.py::test_patch_reset_password": 11.245606623997446, - "src/backend/tests/test_user.py::test_patch_user": 3.2588992070232052, - "src/backend/tests/test_user.py::test_patch_user_wrong_id": 3.3168086239602417, - "src/backend/tests/test_user.py::test_read_all_users": 2.440687207999872, - "src/backend/tests/test_user.py::test_user_waiting_for_approval": 9.475323291990208, - "src/backend/tests/test_webhook.py::test_webhook_endpoint": 12.348436542029958, - "src/backend/tests/test_webhook.py::test_webhook_flow_on_run_endpoint": 15.205204916041112, - "src/backend/tests/test_webhook.py::test_webhook_with_random_payload": 8.713364291994367, - "src/backend/tests/unit/components/prompts/test_prompt_component.py::TestPromptComponent::test_post_code_processing": 0.008164541970472783, - "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_invalid_output": 0.000454624998383224, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph": 0.007537708996096626, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional": 0.020996668026782572, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_async_start": 0.009653333021560684, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start": 0.008162209036527202, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start_end": 0.06379004201153293, - "src/backend/tests/unit/graph/graph/test_base.py::test_graph_not_prepared": 0.01988037399132736, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_add_to_vertices_being_run": 2.476791499997489, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled": 2.3258769580570515, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled__wrong": 2.4165378749894444, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_build_run_map": 2.5142138760129455, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict": 2.629594833997544, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_map__bad_case": 8.874073583021527, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_predecessors__bad_case": 2.743527958955383, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_being_run__bad_case": 2.8369890000030864, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_to_run__bad_case": 2.9151457909611054, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable": 8.908991582982708, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_is_active": 2.637443292012904, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_run_predecessors": 2.747438082966255, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_vertices_to_run": 2.8337462919880636, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_pickle": 2.065233791974606, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_from_predecessors": 8.867784041009145, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_vertex_from_runnables": 2.2803797090018634, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_to_dict": 9.405242958950112, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_run_state": 2.4422846660017967, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state": 2.9607737089972943, - "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state__bad_case": 2.485696541989455, - "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_a": 8.826332042983267, - "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_z": 2.949440208991291, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_a": 2.8428027920017485, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_g": 3.315444208041299, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_h": 2.983557416999247, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_invalid_vertex": 1.9296646670263726, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_m": 8.736605707992567, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_n_is_start": 2.5265350410190877, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_t": 2.3543146679585334, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_x": 2.094447916984791, - "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_z": 2.7899541249789763, - "src/backend/tests/unit/graph/test_graph.py::test_build_edges": 2.053889958973741, - "src/backend/tests/unit/graph/test_graph.py::test_build_nodes": 3.264545250014635, - "src/backend/tests/unit/graph/test_graph.py::test_build_params": 1.6351483759935945, - "src/backend/tests/unit/graph/test_graph.py::test_circular_dependencies": 4.829830207978375, - "src/backend/tests/unit/graph/test_graph.py::test_find_last_node": 1.8075883749988861, - "src/backend/tests/unit/graph/test_graph.py::test_get_node": 2.2939607900043484, - "src/backend/tests/unit/graph/test_graph.py::test_get_node_neighbors_basic": 2.5666640420095064, - "src/backend/tests/unit/graph/test_graph.py::test_get_root_vertex": 2.33814408298349, - "src/backend/tests/unit/graph/test_graph.py::test_get_vertices_with_target": 2.0869384160032496, - "src/backend/tests/unit/graph/test_graph.py::test_graph_structure": 3.1825925829762127, - "src/backend/tests/unit/graph/test_graph.py::test_invalid_node_types": 2.1994956269918475, - "src/backend/tests/unit/graph/test_graph.py::test_matched_type": 2.3932184999866877, - "src/backend/tests/unit/graph/test_graph.py::test_pickle_graph": 2.184392209019279, - "src/backend/tests/unit/graph/test_graph.py::test_process_flow": 2.1272420000168495, - "src/backend/tests/unit/graph/test_graph.py::test_process_flow_one_group": 1.9646992909838445, - "src/backend/tests/unit/graph/test_graph.py::test_process_flow_vector_store_grouped": 2.415951082977699, - "src/backend/tests/unit/graph/test_graph.py::test_set_new_target_handle": 1.7951639160164632, - "src/backend/tests/unit/graph/test_graph.py::test_ungroup_node": 2.0279515830043238, - "src/backend/tests/unit/graph/test_graph.py::test_update_source_handle": 2.0684266670432407, - "src/backend/tests/unit/graph/test_graph.py::test_update_target_handle_proxy": 1.5892521249479614, - "src/backend/tests/unit/graph/test_graph.py::test_update_template": 1.7316221670189407, - "src/backend/tests/unit/graph/test_graph.py::test_validate_edges": 1.920275832992047, - "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot": 2.1822710419946816, - "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_components_and_edges": 1.6609269159962423, - "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_structure": 2.5239112499984913, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag": 0.08740387603756972, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump": 0.027446749998489395, - "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump_components_and_edges": 0.026998917979653925, - "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_invalid": 0.0006447900377679616, - "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_valid": 0.00023025000700727105, - "src/backend/tests/unit/inputs/test_inputs.py::test_data_input_valid": 0.0005409169825725257, - "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_invalid": 0.00025333400117233396, - "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_valid": 0.0007362900068983436, - "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_invalid": 0.0009356669906992465, - "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_valid": 0.0004829160461667925, - "src/backend/tests/unit/inputs/test_inputs.py::test_file_input_valid": 0.00024799996754154563, - "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_invalid": 0.00025445802020840347, - "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_valid": 0.00024087497149594128, - "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_invalid": 0.0003797499812208116, - "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_valid": 0.0002122489968314767, - "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_comprehensive": 0.001500750018749386, - "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_invalid": 0.00027112496900372207, - "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_valid": 0.0005940830160398036, - "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_invalid": 0.00029999902471899986, - "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_valid": 0.0004179589741397649, - "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_invalid": 0.0004274170205462724, - "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_valid": 0.0003730839816853404, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_invalid": 0.00033208398963324726, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_valid": 0.0003288750012870878, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_invalid": 0.00023066697758622468, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_valid": 0.00022683301358483732, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_invalid": 0.0002583339810371399, - "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_valid": 0.00027691599098034203, - "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_invalid": 0.00041799998143687844, - "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_valid": 0.004873417055932805, - "src/backend/tests/unit/inputs/test_inputs.py::test_prompt_input_valid": 0.0007432089769281447, - "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_invalid": 0.00033837597584351897, - "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_valid": 0.00022083398653194308, - "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_invalid": 0.0004425400111358613, - "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_valid": 0.0003231659939046949, - "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_invalid": 0.010438332974445075, - "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_valid": 0.0003158329927828163, - "src/backend/tests/unit/schema/test_schema_message.py::test_message_async_prompt_serialization": 0.4811764149926603, - "src/backend/tests/unit/schema/test_schema_message.py::test_message_prompt_serialization": 0.0009208739793393761, - "src/backend/tests/unit/test_api_key.py::test_create_api_key": 2.661131207976723, - "src/backend/tests/unit/test_api_key.py::test_delete_api_key": 2.671240749012213, - "src/backend/tests/unit/test_api_key.py::test_get_api_keys": 2.5765499170229305, - "src/backend/tests/unit/test_cache.py::test_build_graph": 3.0347800820018165, - "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow": 8.144330751005327, - "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_from_request_data": 5.783452290983405, - "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_with_frozen_path": 7.61259591698763, - "src/backend/tests/unit/test_cli.py::test_components_path": 2.2392006250447594, - "src/backend/tests/unit/test_cli.py::test_superuser": 2.476592207007343, - "src/backend/tests/unit/test_custom_component.py::test_build_config_field_keys": 2.0540905419911724, - "src/backend/tests/unit/test_custom_component.py::test_build_config_field_value_keys": 1.9443145420227665, - "src/backend/tests/unit/test_custom_component.py::test_build_config_field_values_dict": 3.319497832970228, - "src/backend/tests/unit/test_custom_component.py::test_build_config_fields_dict": 1.8987905430258252, - "src/backend/tests/unit/test_custom_component.py::test_build_config_has_fields": 2.7343585419876035, - "src/backend/tests/unit/test_custom_component.py::test_build_config_no_code": 1.792709333007224, - "src/backend/tests/unit/test_custom_component.py::test_build_config_return_type": 2.3822355829761364, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_get_tree": 1.952277623990085, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_init": 2.0454807080095634, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_ann_assign": 2.451507583988132, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_no_annotation": 2.353039957990404, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_with_annotation": 1.7640878760430496, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_assign": 2.0938420000020415, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_callable_details_no_args": 1.7373172499937937, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes": 1.7848410429724026, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_init": 2.2325071259983815, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_not_init": 2.4196665409835987, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_functions": 2.178845665999688, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_global_vars": 2.4417894990183413, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_import": 1.707990164984949, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_importfrom": 2.1835803739959374, - "src/backend/tests/unit/test_custom_component.py::test_code_parser_syntax_error": 2.277529457001947, - "src/backend/tests/unit/test_custom_component.py::test_component_code_null_error": 1.9170069170068018, - "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree": 1.8569252080051228, - "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree_syntax_error": 2.174606874003075, - "src/backend/tests/unit/test_custom_component.py::test_component_get_function_valid": 1.9588903749827296, - "src/backend/tests/unit/test_custom_component.py::test_component_init": 2.5707569160149433, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_not_implemented": 2.3832541239680722, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_template_config": 1.767908540990902, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_class_template_validation_no_code": 1.9784962920530234, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_code_tree_syntax_error": 2.0324612080003135, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function": 1.6494328340049833, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args": 1.9188839579874184, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args_no_args": 2.0736736260005273, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type": 2.199567042000126, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type_no_return_type": 1.7597685409709811, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_valid": 2.1681128749914933, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name": 1.658911792008439, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name_no_main_class": 1.4603167499881238, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_init": 2.329375083994819, - "src/backend/tests/unit/test_custom_component.py::test_custom_component_multiple_outputs": 1.8187729580095038, + "src/backend/tests/test_endpoints.py::test_build_vertex_invalid_flow_id": 1.8161861660000795, + "src/backend/tests/test_endpoints.py::test_build_vertex_invalid_vertex_id": 1.6184064170001875, + "src/backend/tests/test_endpoints.py::test_get_all": 3.8724166670003797, + "src/backend/tests/test_endpoints.py::test_get_vertices": 2.3527110839995657, + "src/backend/tests/test_endpoints.py::test_get_vertices_flow_not_found": 1.781084457999441, + "src/backend/tests/test_endpoints.py::test_invalid_flow_id": 1.8902806239998426, + "src/backend/tests/test_endpoints.py::test_invalid_prompt": 1.104316251000455, + "src/backend/tests/test_endpoints.py::test_invalid_run_with_input_type_chat": 1.7665630830001646, + "src/backend/tests/test_endpoints.py::test_post_validate_code": 1.2675197489998027, + "src/backend/tests/test_endpoints.py::test_successful_run_with_input_type_any": 4.90430141699926, + "src/backend/tests/test_endpoints.py::test_successful_run_with_input_type_chat": 5.548072499999762, + "src/backend/tests/test_endpoints.py::test_successful_run_with_input_type_text": 6.992585209000026, + "src/backend/tests/test_endpoints.py::test_successful_run_with_output_type_any": 6.90225587499981, + "src/backend/tests/test_endpoints.py::test_successful_run_with_output_type_debug": 6.666685292999773, + "src/backend/tests/test_endpoints.py::test_successful_run_with_output_type_text": 6.525266874999943, + "src/backend/tests/test_endpoints.py::test_valid_prompt": 1.0876984590004213, + "src/backend/tests/test_endpoints.py::test_various_prompts[The weather is {weather} today.-expected_input_variables1]": 1.0912359599997217, + "src/backend/tests/test_endpoints.py::test_various_prompts[This prompt has no variables.-expected_input_variables2]": 1.0882312500002627, + "src/backend/tests/test_endpoints.py::test_various_prompts[{a}, {b}, and {c} are variables.-expected_input_variables3]": 1.1277481249994707, + "src/backend/tests/test_endpoints.py::test_various_prompts[{color} is my favorite color.-expected_input_variables0]": 1.2742332080001688, + "src/backend/tests/test_messages_endpoints.py::test_delete_messages": 1.5091020420009045, + "src/backend/tests/test_messages_endpoints.py::test_delete_messages_session": 1.510577208000086, + "src/backend/tests/test_messages_endpoints.py::test_update_message": 1.5425646240005335, + "src/backend/tests/test_messages_endpoints.py::test_update_message_not_found": 1.5310995830000138, + "src/backend/tests/test_schema.py::TestInput::test_field_type_str": 0.00023554100016554003, + "src/backend/tests/test_schema.py::TestInput::test_field_type_type": 0.00021875100037505035, + "src/backend/tests/test_schema.py::TestInput::test_input_to_dict": 0.00020670800040534232, + "src/backend/tests/test_schema.py::TestInput::test_invalid_field_type": 0.00023608299989064108, + "src/backend/tests/test_schema.py::TestInput::test_post_process_type_function": 0.00020749999976032996, + "src/backend/tests/test_schema.py::TestInput::test_serialize_field_type": 0.0001787500000318687, + "src/backend/tests/test_schema.py::TestInput::test_validate_type_class": 0.00018495699941922794, + "src/backend/tests/test_schema.py::TestInput::test_validate_type_string": 0.00019104300008621067, + "src/backend/tests/test_schema.py::TestOutput::test_output_add_types": 0.00017562499988343916, + "src/backend/tests/test_schema.py::TestOutput::test_output_default": 0.00018087499984176247, + "src/backend/tests/test_schema.py::TestOutput::test_output_set_selected": 0.00019041600035052397, + "src/backend/tests/test_schema.py::TestOutput::test_output_to_dict": 0.00018754199982140562, + "src/backend/tests/test_schema.py::TestOutput::test_output_validate_display_name": 0.00018495800031814724, + "src/backend/tests/test_schema.py::TestOutput::test_output_validate_model": 0.00020916699941153638, + "src/backend/tests/test_schema.py::TestPostProcessType::test_custom_type": 0.00018125099950339063, + "src/backend/tests/test_schema.py::TestPostProcessType::test_int_type": 0.0001939999992828234, + "src/backend/tests/test_schema.py::TestPostProcessType::test_list_custom_type": 0.00017420900030629127, + "src/backend/tests/test_schema.py::TestPostProcessType::test_list_int_type": 0.00018208300025435165, + "src/backend/tests/test_schema.py::TestPostProcessType::test_union_custom_type": 0.00019349999956830288, + "src/backend/tests/test_schema.py::TestPostProcessType::test_union_type": 0.00017558299941811129, + "src/backend/tests/test_user.py::test_add_user": 1.7881504160000077, + "src/backend/tests/test_user.py::test_data_consistency_after_delete": 1.5578057500001705, + "src/backend/tests/test_user.py::test_data_consistency_after_update": 1.7415219580002486, + "src/backend/tests/test_user.py::test_deactivated_user_cannot_access": 1.7204212500000722, + "src/backend/tests/test_user.py::test_deactivated_user_cannot_login": 1.3222801250003613, + "src/backend/tests/test_user.py::test_delete_user": 1.5755463759996928, + "src/backend/tests/test_user.py::test_delete_user_wrong_id": 2.0808037919996423, + "src/backend/tests/test_user.py::test_inactive_user": 1.3267317489999186, + "src/backend/tests/test_user.py::test_normal_user_cant_delete_user": 1.7230364579995694, + "src/backend/tests/test_user.py::test_normal_user_cant_read_all_users": 1.5408454590001384, + "src/backend/tests/test_user.py::test_patch_reset_password": 2.134319875000074, + "src/backend/tests/test_user.py::test_patch_user": 1.5280406670003686, + "src/backend/tests/test_user.py::test_patch_user_wrong_id": 1.5431871670002693, + "src/backend/tests/test_user.py::test_read_all_users": 1.3631829999994807, + "src/backend/tests/test_user.py::test_user_waiting_for_approval": 1.7217974579998554, + "src/backend/tests/test_webhook.py::test_webhook_endpoint": 8.848518459000388, + "src/backend/tests/test_webhook.py::test_webhook_flow_on_run_endpoint": 4.675444458000584, + "src/backend/tests/test_webhook.py::test_webhook_with_random_payload": 5.161753501000476, + "src/backend/tests/unit/api/test_api_utils.py::test_get_outdated_components": 1.1425726240004224, + "src/backend/tests/unit/api/test_api_utils.py::test_get_suggestion_message": 1.7624517080002988, + "src/backend/tests/unit/components/prompts/test_prompt_component.py::TestPromptComponent::test_post_code_processing": 0.0013335419994291442, + "src/backend/tests/unit/custom/custom_component/test_component.py::test_set_invalid_output": 0.00042366600018795, + "src/backend/tests/unit/exceptions/test_api.py::test_api_exception": 1.1389405410004656, + "src/backend/tests/unit/exceptions/test_api.py::test_api_exception_no_flow": 1.152361500999632, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph": 0.010227958999621478, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional": 0.010483666000254743, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_async_start": 0.011119625999981508, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start": 0.011757042999761325, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_functional_start_end": 0.023863916999744106, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_not_prepared": 0.017605375000130152, + "src/backend/tests/unit/graph/graph/test_base.py::test_graph_with_edge": 0.011328083999615046, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_add_to_vertices_being_run": 1.1834923340002206, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled": 1.1408760409995011, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_are_all_predecessors_fulfilled__wrong": 1.9469006670001363, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_build_run_map": 1.1431461249999302, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict": 1.1375733340000806, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_map__bad_case": 1.1561181250003756, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_run_predecessors__bad_case": 1.1398941239999658, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_being_run__bad_case": 1.1692108339998413, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_from_dict_without_vertices_to_run__bad_case": 1.8318268329999228, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable": 1.1589285000000018, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_is_active": 1.1601161660000798, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_run_predecessors": 1.1846444589996281, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_is_vertex_runnable__wrong_vertices_to_run": 1.156324541000231, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_pickle": 1.1490497489999143, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_from_predecessors": 1.17061966700021, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_remove_vertex_from_runnables": 1.1513506659998711, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_to_dict": 1.0720182910004041, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_run_state": 1.152722833000098, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state": 1.1509739160001118, + "src/backend/tests/unit/graph/graph/test_runnable_vertices_manager.py::test_update_vertex_run_state__bad_case": 1.1737124579994997, + "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_a": 1.1541486669998449, + "src/backend/tests/unit/graph/graph/test_utils.py::test_get_successors_z": 1.152469375999317, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_a": 1.2232545840001876, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_g": 1.2133457499999167, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_h": 1.206607958999939, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_invalid_vertex": 1.2181356250002864, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_m": 1.2004494170000726, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_n_is_start": 1.1721724999997605, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_t": 1.233656458999576, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_x": 2.1063177910000377, + "src/backend/tests/unit/graph/graph/test_utils.py::test_sort_up_to_vertex_z": 1.1587511679999807, + "src/backend/tests/unit/graph/test_graph.py::test_build_edges": 1.2613786659999278, + "src/backend/tests/unit/graph/test_graph.py::test_build_nodes": 1.236596333000307, + "src/backend/tests/unit/graph/test_graph.py::test_build_params": 1.1992265409999163, + "src/backend/tests/unit/graph/test_graph.py::test_circular_dependencies": 1.2119703740004297, + "src/backend/tests/unit/graph/test_graph.py::test_find_last_node": 1.1726129999997283, + "src/backend/tests/unit/graph/test_graph.py::test_get_node": 1.2559688749997804, + "src/backend/tests/unit/graph/test_graph.py::test_get_node_neighbors_basic": 2.205406665999817, + "src/backend/tests/unit/graph/test_graph.py::test_get_root_vertex": 1.2303205419998449, + "src/backend/tests/unit/graph/test_graph.py::test_get_vertices_with_target": 1.2130661250002959, + "src/backend/tests/unit/graph/test_graph.py::test_graph_structure": 1.226738209999894, + "src/backend/tests/unit/graph/test_graph.py::test_invalid_node_types": 1.218165749000491, + "src/backend/tests/unit/graph/test_graph.py::test_matched_type": 1.2096231260002241, + "src/backend/tests/unit/graph/test_graph.py::test_pickle_graph": 1.1924404169999434, + "src/backend/tests/unit/graph/test_graph.py::test_process_flow": 1.198885958000119, + "src/backend/tests/unit/graph/test_graph.py::test_process_flow_one_group": 1.1860583339998811, + "src/backend/tests/unit/graph/test_graph.py::test_process_flow_vector_store_grouped": 1.1757077080001181, + "src/backend/tests/unit/graph/test_graph.py::test_set_new_target_handle": 1.2467699590001757, + "src/backend/tests/unit/graph/test_graph.py::test_ungroup_node": 1.200591749999603, + "src/backend/tests/unit/graph/test_graph.py::test_update_source_handle": 1.2464087500002279, + "src/backend/tests/unit/graph/test_graph.py::test_update_target_handle_proxy": 1.2738795420000315, + "src/backend/tests/unit/graph/test_graph.py::test_update_template": 2.3875174170002538, + "src/backend/tests/unit/graph/test_graph.py::test_validate_edges": 1.2202941670002474, + "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot": 1.2400888339998346, + "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_components_and_edges": 1.2050360830003228, + "src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py::test_memory_chatbot_dump_structure": 1.226008957999511, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag": 0.07454762600036702, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_add": 0.06540679199997612, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump": 0.03427487499993731, + "src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py::test_vector_store_rag_dump_components_and_edges": 0.03775970900005632, + "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_invalid": 0.0001977499996428378, + "src/backend/tests/unit/inputs/test_inputs.py::test_bool_input_valid": 0.0003290839995315764, + "src/backend/tests/unit/inputs/test_inputs.py::test_data_input_valid": 0.0005500840002241603, + "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_invalid": 0.00021833400023751892, + "src/backend/tests/unit/inputs/test_inputs.py::test_dict_input_valid": 0.0002739589999691816, + "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_invalid": 0.00028154100027677487, + "src/backend/tests/unit/inputs/test_inputs.py::test_dropdown_input_valid": 0.00018683299958865973, + "src/backend/tests/unit/inputs/test_inputs.py::test_file_input_valid": 0.0005212080000092101, + "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_invalid": 0.00016741600029490655, + "src/backend/tests/unit/inputs/test_inputs.py::test_float_input_valid": 0.0001750840001477627, + "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_invalid": 0.00030137500016280683, + "src/backend/tests/unit/inputs/test_inputs.py::test_handle_input_valid": 0.00028079100002287305, + "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_comprehensive": 0.00022679100038658362, + "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_invalid": 0.0002707490002649138, + "src/backend/tests/unit/inputs/test_inputs.py::test_instantiate_input_valid": 0.000520291999237088, + "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_invalid": 0.000933000999793876, + "src/backend/tests/unit/inputs/test_inputs.py::test_int_input_valid": 0.00018916599947260693, + "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_invalid": 0.0002494579998710833, + "src/backend/tests/unit/inputs/test_inputs.py::test_message_text_input_valid": 0.00048650000053385156, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_invalid": 0.0018433750001349836, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_input_valid": 0.0006555419995493139, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_invalid": 0.0003428750001148728, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiline_secret_input_valid": 0.001843831999849499, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_invalid": 0.00019454099992799456, + "src/backend/tests/unit/inputs/test_inputs.py::test_multiselect_input_valid": 0.0003385830000297574, + "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_invalid": 0.00046958300026744837, + "src/backend/tests/unit/inputs/test_inputs.py::test_nested_dict_input_valid": 0.0007980010000210314, + "src/backend/tests/unit/inputs/test_inputs.py::test_prompt_input_valid": 0.0006562079997820547, + "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_invalid": 0.00018541600002208725, + "src/backend/tests/unit/inputs/test_inputs.py::test_secret_str_input_valid": 0.00017520799929116038, + "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_invalid": 0.00023595900029249606, + "src/backend/tests/unit/inputs/test_inputs.py::test_str_input_valid": 0.00019979200033048983, + "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_invalid": 0.00026916500019069645, + "src/backend/tests/unit/inputs/test_inputs.py::test_table_input_valid": 0.00023066599987942027, + "src/backend/tests/unit/schema/test_schema_message.py::test_message_async_prompt_serialization": 0.2658414169995922, + "src/backend/tests/unit/schema/test_schema_message.py::test_message_prompt_serialization": 0.0005853330003446899, + "src/backend/tests/unit/test_api_key.py::test_create_api_key": 1.668041834000178, + "src/backend/tests/unit/test_api_key.py::test_delete_api_key": 1.6283347500002492, + "src/backend/tests/unit/test_api_key.py::test_get_api_keys": 1.5674538329994903, + "src/backend/tests/unit/test_cache.py::test_build_graph": 1.1988659180001378, + "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow": 5.618974041000001, + "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_from_request_data": 5.709847209000145, + "src/backend/tests/unit/test_chat_endpoint.py::test_build_flow_with_frozen_path": 6.376642749999974, + "src/backend/tests/unit/test_cli.py::test_components_path": 1.2666867920006553, + "src/backend/tests/unit/test_cli.py::test_superuser": 1.6751555829996505, + "src/backend/tests/unit/test_custom_component.py::test_build_config_field_keys": 0.2042501250002715, + "src/backend/tests/unit/test_custom_component.py::test_build_config_field_value_keys": 0.19506029200010744, + "src/backend/tests/unit/test_custom_component.py::test_build_config_field_values_dict": 0.1966598340004566, + "src/backend/tests/unit/test_custom_component.py::test_build_config_fields_dict": 0.20268266700031745, + "src/backend/tests/unit/test_custom_component.py::test_build_config_has_fields": 0.1948232069998994, + "src/backend/tests/unit/test_custom_component.py::test_build_config_no_code": 0.000207583999781491, + "src/backend/tests/unit/test_custom_component.py::test_build_config_return_type": 0.19622991700043713, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_get_tree": 0.00027658399994834326, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_init": 0.0002327079996575776, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_ann_assign": 0.00019754200002353173, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_no_annotation": 0.00018179199969381443, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_arg_with_annotation": 0.0001805840006454673, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_assign": 0.0002079589999084419, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_callable_details_no_args": 0.0001973339999494783, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes": 0.00032654200003889855, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_classes_raises": 0.0002649169996402634, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_init": 0.00018600100020194077, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_function_def_not_init": 0.00019508200011841836, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_functions": 0.00021508300005734782, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_global_vars": 0.00020745999972859863, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_import": 0.00025808299960772274, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_parse_imports_importfrom": 0.00018808400045600138, + "src/backend/tests/unit/test_custom_component.py::test_code_parser_syntax_error": 0.001801750000595348, + "src/backend/tests/unit/test_custom_component.py::test_component_code_null_error": 0.0001859579997471883, + "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree": 0.0006688740004392457, + "src/backend/tests/unit/test_custom_component.py::test_component_get_code_tree_syntax_error": 0.0002742089995990682, + "src/backend/tests/unit/test_custom_component.py::test_component_get_function_valid": 0.00021645800006808713, + "src/backend/tests/unit/test_custom_component.py::test_component_init": 0.00018979200012836372, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_not_implemented": 0.00019016700025531463, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_build_template_config": 0.00036833300055150175, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_class_template_validation_no_code": 0.00018979200012836372, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_code_tree_syntax_error": 0.0002411679997749161, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function": 0.00022095800022725598, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args": 0.0006820000003244786, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_args_no_args": 0.0003158340000481985, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type": 0.0005181660003472643, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_entrypoint_return_type_no_return_type": 0.00031354199927591253, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_function_valid": 0.00019329300039316877, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name": 0.0005222909999247349, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_get_main_class_name_no_main_class": 0.00021295800024745404, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_init": 0.0001823750003495661, + "src/backend/tests/unit/test_custom_component.py::test_custom_component_multiple_outputs": 0.19756641600042713, "src/backend/tests/unit/test_custom_component.py::test_list_flows_flow_objects": 1.981454541994026, - "src/backend/tests/unit/test_custom_component.py::test_list_flows_return_type": 1.8733046670095064, - "src/backend/tests/unit/test_data_class.py::test_add_method_for_integers": 1.7596916669863276, - "src/backend/tests/unit/test_data_class.py::test_add_method_for_strings": 2.099744750012178, - "src/backend/tests/unit/test_data_class.py::test_add_method_with_non_overlapping_keys": 2.0075557490054052, - "src/backend/tests/unit/test_data_class.py::test_conversion_from_document": 1.9583345409773756, - "src/backend/tests/unit/test_data_class.py::test_conversion_to_document": 1.953191417036578, - "src/backend/tests/unit/test_data_class.py::test_custom_attribute_get_set_del": 2.8074895000027027, - "src/backend/tests/unit/test_data_class.py::test_custom_attribute_setting_and_getting": 1.744376168033341, - "src/backend/tests/unit/test_data_class.py::test_data_initialization": 2.3415857510408387, - "src/backend/tests/unit/test_data_class.py::test_deep_copy": 1.5598407920042519, - "src/backend/tests/unit/test_data_class.py::test_dir_includes_data_keys": 2.4137807070219424, - "src/backend/tests/unit/test_data_class.py::test_dir_reflects_attribute_deletion": 1.8897194170276634, - "src/backend/tests/unit/test_data_class.py::test_get_text_with_empty_data": 2.669506582984468, - "src/backend/tests/unit/test_data_class.py::test_get_text_with_none_data": 1.8896955420204904, - "src/backend/tests/unit/test_data_class.py::test_get_text_with_text_key": 1.953111374983564, - "src/backend/tests/unit/test_data_class.py::test_get_text_without_text_key": 1.9460047910106368, - "src/backend/tests/unit/test_data_class.py::test_str_and_dir_methods": 2.6938894579943735, - "src/backend/tests/unit/test_data_class.py::test_validate_data_with_extra_keys": 1.9336464170191903, - "src/backend/tests/unit/test_data_components.py::test_build_with_multiple_urls": 0.026251333008985966, - "src/backend/tests/unit/test_data_components.py::test_directory_component_build_with_multithreading": 0.0020231239905115217, - "src/backend/tests/unit/test_data_components.py::test_directory_without_mocks": 0.38643029099330306, - "src/backend/tests/unit/test_data_components.py::test_failed_request": 0.011844915978144854, - "src/backend/tests/unit/test_data_components.py::test_parse_curl": 0.0004114170151297003, - "src/backend/tests/unit/test_data_components.py::test_successful_get_request": 0.015994457993656397, - "src/backend/tests/unit/test_data_components.py::test_timeout": 0.01364304099115543, - "src/backend/tests/unit/test_data_components.py::test_url_component": 0.5631265829724725, - "src/backend/tests/unit/test_database.py::test_create_flow": 2.5350006250082515, - "src/backend/tests/unit/test_database.py::test_create_flow_with_invalid_data": 2.6853410840267316, - "src/backend/tests/unit/test_database.py::test_create_flows": 3.4552309999999125, - "src/backend/tests/unit/test_database.py::test_delete_flow": 4.201302792993374, - "src/backend/tests/unit/test_database.py::test_delete_flows": 2.73355954195722, - "src/backend/tests/unit/test_database.py::test_delete_flows_with_transaction_and_build": 3.3879740410193335, - "src/backend/tests/unit/test_database.py::test_delete_nonexistent_flow": 2.906195083982311, - "src/backend/tests/unit/test_database.py::test_download_file": 2.6433084169693757, - "src/backend/tests/unit/test_database.py::test_get_nonexistent_flow": 2.9141747919784393, - "src/backend/tests/unit/test_database.py::test_load_flows": 2.3472657920210622, - "src/backend/tests/unit/test_database.py::test_migrate_transactions": 2.4188965820358135, - "src/backend/tests/unit/test_database.py::test_migrate_transactions_no_duckdb": 2.4176759159890935, - "src/backend/tests/unit/test_database.py::test_read_flow": 2.524181623972254, - "src/backend/tests/unit/test_database.py::test_read_flows": 3.3437811249750666, - "src/backend/tests/unit/test_database.py::test_read_only_starter_projects": 2.8177391680073924, - "src/backend/tests/unit/test_database.py::test_sqlite_pragmas": 2.2383368749869987, - "src/backend/tests/unit/test_database.py::test_update_flow": 3.1579460009816103, - "src/backend/tests/unit/test_database.py::test_update_flow_idempotency": 2.9125417500035837, - "src/backend/tests/unit/test_database.py::test_update_nonexistent_flow": 2.838372750993585, - "src/backend/tests/unit/test_database.py::test_upload_file": 2.6103912079997826, - "src/backend/tests/unit/test_experimental_components.py::test_python_function_component": 2.076999415992759, - "src/backend/tests/unit/test_files.py::test_delete_file": 2.799217874009628, - "src/backend/tests/unit/test_files.py::test_download_file": 2.51829199999338, - "src/backend/tests/unit/test_files.py::test_file_operations": 3.3802113739948254, - "src/backend/tests/unit/test_files.py::test_list_files": 2.7689662509947084, - "src/backend/tests/unit/test_files.py::test_upload_file": 3.3243832079751883, - "src/backend/tests/unit/test_frontend_nodes.py::test_frontend_node_to_dict": 2.1432127919979393, - "src/backend/tests/unit/test_frontend_nodes.py::test_template_field_defaults": 2.2584460410289466, - "src/backend/tests/unit/test_frontend_nodes.py::test_template_to_dict": 2.8126436240272596, - "src/backend/tests/unit/test_helper_components.py::test_data_as_text_component": 2.214979208976729, - "src/backend/tests/unit/test_helper_components.py::test_uuid_generator_component": 3.2323666680022143, - "src/backend/tests/unit/test_initial_setup.py::test_create_or_update_starter_projects": 2.2390285000146832, - "src/backend/tests/unit/test_initial_setup.py::test_get_project_data": 2.6407637080119457, - "src/backend/tests/unit/test_initial_setup.py::test_load_starter_projects": 2.2846807509777136, - "src/backend/tests/unit/test_initial_setup.py::test_refresh_starter_projects": 5.397776041994803, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_create_secret": 2.1846052090113517, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_delete_secret": 2.4931142500427086, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_email_address": 3.9143964989925735, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_string": 2.658771957969293, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_uuid": 3.536810541001614, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_ends_with_non_alphanumeric": 2.6400313759804703, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_get_secret": 2.2426519600267056, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_long_string": 2.5769125409715343, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_starts_with_non_alphanumeric": 2.9591099170211237, - "src/backend/tests/unit/test_kubernetes_secrets.py::test_uuid_case_insensitivity": 2.8027220430085436, - "src/backend/tests/unit/test_loading.py::test_load_flow_from_json": 2.0592095839965623, - "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_object": 0.05121591599890962, - "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_with_tweaks": 2.3594004999904428, - "src/backend/tests/unit/test_logger.py::test_enabled": 2.366979167010868, - "src/backend/tests/unit/test_logger.py::test_get_after_timestamp": 2.843343543005176, - "src/backend/tests/unit/test_logger.py::test_get_before_timestamp": 2.0495081240078434, - "src/backend/tests/unit/test_logger.py::test_get_last_n": 3.3493437920114957, - "src/backend/tests/unit/test_logger.py::test_init_default": 4.3632337910239585, - "src/backend/tests/unit/test_logger.py::test_init_with_env_variable": 2.747672124998644, - "src/backend/tests/unit/test_logger.py::test_len": 3.0128796670178417, - "src/backend/tests/unit/test_logger.py::test_max_size": 2.5830446239560843, - "src/backend/tests/unit/test_logger.py::test_write": 3.656159915990429, - "src/backend/tests/unit/test_logger.py::test_write_overflow": 4.817402709042653, - "src/backend/tests/unit/test_login.py::test_login_successful": 3.081307165994076, - "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_password": 3.23898391702096, - "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_username": 2.315841832984006, - "src/backend/tests/unit/test_messages.py::test_add_messages": 2.261571748997085, - "src/backend/tests/unit/test_messages.py::test_add_messagetables": 2.3893967490294017, - "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[convert_to_langchain_type]": 3.2416470000171103, - "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[message]": 2.004590749013005, - "src/backend/tests/unit/test_messages.py::test_delete_messages": 2.3871561660198495, - "src/backend/tests/unit/test_messages.py::test_get_messages": 2.0157879999896977, - "src/backend/tests/unit/test_messages.py::test_store_message": 1.9027003319642972, - "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_cached_session": 2.2040907500195317, - "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_no_cached_session": 2.883888084004866, - "src/backend/tests/unit/test_process.py::test_load_langchain_object_without_session_id": 2.329415375017561, - "src/backend/tests/unit/test_process.py::test_multiple_tweaks": 2.047054874972673, - "src/backend/tests/unit/test_process.py::test_no_tweaks": 1.952550498972414, - "src/backend/tests/unit/test_process.py::test_single_tweak": 2.148759791016346, - "src/backend/tests/unit/test_process.py::test_tweak_no_node_id": 3.1278445839998312, - "src/backend/tests/unit/test_process.py::test_tweak_not_in_template": 2.113566374988295, - "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_default_superuser": 2.256663625012152, - "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_no_default_superuser": 2.211974083009409, - "src/backend/tests/unit/test_telemetry.py::test_gauge": 2.4124685839633457, - "src/backend/tests/unit/test_telemetry.py::test_gauge_with_counter_method": 2.186464792001061, - "src/backend/tests/unit/test_telemetry.py::test_gauge_with_historgram_method": 2.6113254159863573, - "src/backend/tests/unit/test_telemetry.py::test_gauge_with_up_down_counter_method": 2.225708084035432, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter": 2.130592000001343, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter_empty_label": 2.2976541249954607, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter_missing_mandatory_label": 2.443581625993829, - "src/backend/tests/unit/test_telemetry.py::test_increment_counter_unregisted_metric": 2.630037208989961, - "src/backend/tests/unit/test_telemetry.py::test_init": 2.1476573330292013, - "src/backend/tests/unit/test_telemetry.py::test_missing_labels": 2.3570764580217656, - "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton": 2.390594750002492, - "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton_race_condition": 2.437567832006607, - "src/backend/tests/unit/test_telemetry.py::test_opentelementry_singleton": 3.0913529580284376, - "src/backend/tests/unit/test_template.py::test_build_template_from_function": 2.9347434579976834, - "src/backend/tests/unit/test_template.py::test_get_base_classes": 2.118878333014436, - "src/backend/tests/unit/test_template.py::test_get_default_factory": 2.143760042003123, - "src/backend/tests/unit/test_validate_code.py::test_create_function": 2.2330028339929413, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_function": 1.7509510009840596, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_module": 1.850503541965736, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_schema": 1.81301379200886, - "src/backend/tests/unit/test_validate_code.py::test_execute_function_success": 2.074162457982311, - "src/backend/tests/unit/test_validate_code.py::test_validate_code": 2.2336132919881493, - "src/backend/tests/unit/test_version.py::test_compute_main": 2.139949625969166, - "src/backend/tests/unit/test_version.py::test_version": 1.6260940420324914, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol::password@host-protocol::password@host]": 0.0012022080190945417, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa:ss:word@host-protocol:user:pa:ss:word@host]": 0.000848833005875349, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa@ss@word@host-protocol:user:pa%40ss%40word@host]": 0.001619456976186484, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pass@word@host-protocol:user:pass%40word@host]": 0.000586748996283859, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@-protocol:user:password@]": 0.0025060399784706533, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@host-protocol:user:password@host]": 0.0007859579636715353, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user@host-protocol:user@host]": 0.0004099990183021873, - "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[user:password@host-user:password@host]": 0.0005190849769860506 + "src/backend/tests/unit/test_custom_component.py::test_list_flows_return_type": 0.2607629169997381, + "src/backend/tests/unit/test_custom_component_with_client.py::test_list_flows_flow_objects": 1.306553499999609, + "src/backend/tests/unit/test_data_class.py::test_add_method_for_integers": 1.1870880830006172, + "src/backend/tests/unit/test_data_class.py::test_add_method_for_strings": 1.1892704990000311, + "src/backend/tests/unit/test_data_class.py::test_add_method_with_non_overlapping_keys": 1.1890747499996905, + "src/backend/tests/unit/test_data_class.py::test_conversion_from_document": 1.2196368339996297, + "src/backend/tests/unit/test_data_class.py::test_conversion_to_document": 1.1908134569998765, + "src/backend/tests/unit/test_data_class.py::test_custom_attribute_get_set_del": 1.1929801669998596, + "src/backend/tests/unit/test_data_class.py::test_custom_attribute_setting_and_getting": 1.1900344170003336, + "src/backend/tests/unit/test_data_class.py::test_data_initialization": 1.2146948340000563, + "src/backend/tests/unit/test_data_class.py::test_deep_copy": 1.1954752910000934, + "src/backend/tests/unit/test_data_class.py::test_dir_includes_data_keys": 2.760468000000401, + "src/backend/tests/unit/test_data_class.py::test_dir_reflects_attribute_deletion": 1.2251055420001649, + "src/backend/tests/unit/test_data_class.py::test_get_text_with_empty_data": 1.2183382499997606, + "src/backend/tests/unit/test_data_class.py::test_get_text_with_none_data": 1.2144034150001062, + "src/backend/tests/unit/test_data_class.py::test_get_text_with_text_key": 1.2315653749997182, + "src/backend/tests/unit/test_data_class.py::test_get_text_without_text_key": 1.2123793340001612, + "src/backend/tests/unit/test_data_class.py::test_str_and_dir_methods": 1.2003138760001093, + "src/backend/tests/unit/test_data_class.py::test_validate_data_with_extra_keys": 1.1931614580003043, + "src/backend/tests/unit/test_data_components.py::test_build_with_multiple_urls": 0.005447873999855801, + "src/backend/tests/unit/test_data_components.py::test_directory_component_build_with_multithreading": 0.0007867510003052303, + "src/backend/tests/unit/test_data_components.py::test_directory_without_mocks": 0.25596470800019233, + "src/backend/tests/unit/test_data_components.py::test_failed_request": 0.00482766699997228, + "src/backend/tests/unit/test_data_components.py::test_parse_curl": 0.0003262509999331087, + "src/backend/tests/unit/test_data_components.py::test_successful_get_request": 0.0054463749997921695, + "src/backend/tests/unit/test_data_components.py::test_timeout": 0.004433167000115645, + "src/backend/tests/unit/test_data_components.py::test_url_component": 0.4809711249999964, + "src/backend/tests/unit/test_database.py::test_create_flow": 1.6828834990005817, + "src/backend/tests/unit/test_database.py::test_create_flow_with_invalid_data": 1.6774957920001725, + "src/backend/tests/unit/test_database.py::test_create_flows": 1.673384124999302, + "src/backend/tests/unit/test_database.py::test_delete_flow": 1.6573062500006017, + "src/backend/tests/unit/test_database.py::test_delete_flows": 1.7055409169997802, + "src/backend/tests/unit/test_database.py::test_delete_flows_with_transaction_and_build": 1.7174484999995911, + "src/backend/tests/unit/test_database.py::test_delete_nonexistent_flow": 1.6730859590002183, + "src/backend/tests/unit/test_database.py::test_download_file": 3.4064119170002414, + "src/backend/tests/unit/test_database.py::test_get_nonexistent_flow": 1.6842376250001507, + "src/backend/tests/unit/test_database.py::test_load_flows": 1.3783337080003548, + "src/backend/tests/unit/test_database.py::test_migrate_transactions": 1.40214775000004, + "src/backend/tests/unit/test_database.py::test_migrate_transactions_no_duckdb": 1.364264834000096, + "src/backend/tests/unit/test_database.py::test_read_flow": 1.6665177079999012, + "src/backend/tests/unit/test_database.py::test_read_flows": 1.7203122079999957, + "src/backend/tests/unit/test_database.py::test_read_only_starter_projects": 1.7277780829995208, + "src/backend/tests/unit/test_database.py::test_sqlite_pragmas": 1.3023467509997317, + "src/backend/tests/unit/test_database.py::test_update_flow": 1.6924472499999865, + "src/backend/tests/unit/test_database.py::test_update_flow_idempotency": 1.7028064580003957, + "src/backend/tests/unit/test_database.py::test_update_nonexistent_flow": 1.7007274170000528, + "src/backend/tests/unit/test_database.py::test_upload_file": 1.6786055420002413, + "src/backend/tests/unit/test_experimental_components.py::test_python_function_component": 1.3152118330008307, + "src/backend/tests/unit/test_files.py::test_delete_file": 1.8698114990006616, + "src/backend/tests/unit/test_files.py::test_download_file": 1.8618997500002479, + "src/backend/tests/unit/test_files.py::test_file_operations": 1.8774095419998957, + "src/backend/tests/unit/test_files.py::test_list_files": 1.8360633750003217, + "src/backend/tests/unit/test_files.py::test_upload_file": 1.8361791669995, + "src/backend/tests/unit/test_frontend_nodes.py::test_frontend_node_to_dict": 1.2789837089994762, + "src/backend/tests/unit/test_frontend_nodes.py::test_template_field_defaults": 1.4903630839994548, + "src/backend/tests/unit/test_frontend_nodes.py::test_template_to_dict": 3.1915530399996896, + "src/backend/tests/unit/test_helper_components.py::test_data_as_text_component": 1.2936121669995373, + "src/backend/tests/unit/test_helper_components.py::test_uuid_generator_component": 1.2925201660000312, + "src/backend/tests/unit/test_initial_setup.py::test_create_or_update_starter_projects": 1.3174052090002988, + "src/backend/tests/unit/test_initial_setup.py::test_get_project_data": 1.3585754989994712, + "src/backend/tests/unit/test_initial_setup.py::test_load_starter_projects": 1.2834318330001224, + "src/backend/tests/unit/test_initial_setup.py::test_refresh_starter_projects": 2.5242092499997852, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_create_secret": 1.277097417000732, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_delete_secret": 1.2887339569997494, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_email_address": 1.3158049170001505, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_string": 1.2695311679999577, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_encode_uuid": 1.259859792000043, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_ends_with_non_alphanumeric": 1.302864874999159, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_get_secret": 1.2660873319996426, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_long_string": 1.2887962920003702, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_starts_with_non_alphanumeric": 1.3552359580003213, + "src/backend/tests/unit/test_kubernetes_secrets.py::test_uuid_case_insensitivity": 1.299499248000302, + "src/backend/tests/unit/test_loading.py::test_load_flow_from_json": 0.0960882499998661, + "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_object": 0.018880542000260903, + "src/backend/tests/unit/test_loading.py::test_load_flow_from_json_with_tweaks": 0.016509083999608265, + "src/backend/tests/unit/test_logger.py::test_enabled": 1.0503544179996425, + "src/backend/tests/unit/test_logger.py::test_get_after_timestamp": 1.057395667000037, + "src/backend/tests/unit/test_logger.py::test_get_before_timestamp": 1.069635374000427, + "src/backend/tests/unit/test_logger.py::test_get_last_n": 1.0638129170001775, + "src/backend/tests/unit/test_logger.py::test_init_default": 3.221781582999938, + "src/backend/tests/unit/test_logger.py::test_init_with_env_variable": 1.1272078760002842, + "src/backend/tests/unit/test_logger.py::test_len": 1.0672918339996613, + "src/backend/tests/unit/test_logger.py::test_max_size": 1.0532803749997584, + "src/backend/tests/unit/test_logger.py::test_write": 1.083741292999548, + "src/backend/tests/unit/test_logger.py::test_write_overflow": 1.0727743750007903, + "src/backend/tests/unit/test_login.py::test_login_successful": 1.495486374999473, + "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_password": 1.2617856669999128, + "src/backend/tests/unit/test_login.py::test_login_unsuccessful_wrong_username": 1.0718942079993212, + "src/backend/tests/unit/test_messages.py::test_add_messages": 1.0813413340001716, + "src/backend/tests/unit/test_messages.py::test_add_messagetables": 1.064473082999939, + "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[convert_to_langchain_type]": 1.0604117919997407, + "src/backend/tests/unit/test_messages.py::test_convert_to_langchain[message]": 1.0689540419998593, + "src/backend/tests/unit/test_messages.py::test_delete_messages": 1.1744598340001176, + "src/backend/tests/unit/test_messages.py::test_get_messages": 1.0707767070002774, + "src/backend/tests/unit/test_messages.py::test_store_message": 1.0799672490002195, + "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_cached_session": 1.0705989160001081, + "src/backend/tests/unit/test_process.py::test_load_langchain_object_with_no_cached_session": 1.0660591649993876, + "src/backend/tests/unit/test_process.py::test_load_langchain_object_without_session_id": 1.0685117090001768, + "src/backend/tests/unit/test_process.py::test_multiple_tweaks": 3.4656270829996174, + "src/backend/tests/unit/test_process.py::test_no_tweaks": 1.0528393340005096, + "src/backend/tests/unit/test_process.py::test_single_tweak": 1.0588130830001319, + "src/backend/tests/unit/test_process.py::test_tweak_no_node_id": 1.0907960420004201, + "src/backend/tests/unit/test_process.py::test_tweak_not_in_template": 1.065006457999516, + "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_default_superuser": 1.0545740010002191, + "src/backend/tests/unit/test_setup_superuser.py::test_teardown_superuser_no_default_superuser": 1.054325458999756, + "src/backend/tests/unit/test_telemetry.py::test_gauge": 1.081740833999902, + "src/backend/tests/unit/test_telemetry.py::test_gauge_with_counter_method": 1.056230042000152, + "src/backend/tests/unit/test_telemetry.py::test_gauge_with_historgram_method": 1.110880250000264, + "src/backend/tests/unit/test_telemetry.py::test_gauge_with_up_down_counter_method": 1.1089114589999554, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter": 1.091222834000746, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter_empty_label": 1.0598395000001801, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter_missing_mandatory_label": 1.0523453739997422, + "src/backend/tests/unit/test_telemetry.py::test_increment_counter_unregisted_metric": 1.060642084000392, + "src/backend/tests/unit/test_telemetry.py::test_init": 1.0586442910002916, + "src/backend/tests/unit/test_telemetry.py::test_missing_labels": 1.0542013740000584, + "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton": 1.0596915410001202, + "src/backend/tests/unit/test_telemetry.py::test_multithreaded_singleton_race_condition": 1.078360167000028, + "src/backend/tests/unit/test_telemetry.py::test_opentelementry_singleton": 1.0586542500004725, + "src/backend/tests/unit/test_template.py::test_build_template_from_function": 1.0667507910002314, + "src/backend/tests/unit/test_template.py::test_get_base_classes": 1.0488440839994837, + "src/backend/tests/unit/test_template.py::test_get_default_factory": 1.061691208999946, + "src/backend/tests/unit/test_validate_code.py::test_create_function": 3.7202681249996203, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_function": 1.0697480410003664, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_module": 1.0638055839999652, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_missing_schema": 1.0542077080003764, + "src/backend/tests/unit/test_validate_code.py::test_execute_function_success": 1.0714820839998538, + "src/backend/tests/unit/test_validate_code.py::test_validate_code": 1.0665104990002874, + "src/backend/tests/unit/test_version.py::test_compute_main": 1.0618379169995933, + "src/backend/tests/unit/test_version.py::test_version": 1.0611935000001722, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol::password@host-protocol::password@host]": 0.0002089159997922252, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa:ss:word@host-protocol:user:pa:ss:word@host]": 0.0002251249998153071, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pa@ss@word@host-protocol:user:pa%40ss%40word@host]": 0.0002600429997983156, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:pass@word@host-protocol:user:pass%40word@host]": 0.0002567910000834672, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@-protocol:user:password@]": 0.0002196240002376726, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user:password@host-protocol:user:password@host]": 0.0002557500001785229, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[protocol:user@host-protocol:user@host]": 0.00021862499943381408, + "src/backend/tests/unit/utils/test_connection_string_parser.py::test_transform_connection_string[user:password@host-user:password@host]": 0.0002157910003006691 } \ No newline at end of file diff --git a/src/backend/tests/unit/inputs/test_inputs.py b/src/backend/tests/unit/inputs/test_inputs.py index 902d8142231f..b409e82cacae 100644 --- a/src/backend/tests/unit/inputs/test_inputs.py +++ b/src/backend/tests/unit/inputs/test_inputs.py @@ -20,7 +20,7 @@ SecretStrInput, StrInput, TableInput, - _instantiate_input, + instantiate_input, ) from langflow.schema.message import Message @@ -69,14 +69,14 @@ def test_message_text_input_invalid(): def test_instantiate_input_valid(): data = {"name": "valid_input", "value": "This is a string"} - input_instance = _instantiate_input("StrInput", data) + input_instance = instantiate_input("StrInput", data) assert isinstance(input_instance, StrInput) assert input_instance.value == "This is a string" def test_instantiate_input_invalid(): with pytest.raises(ValueError): - _instantiate_input("InvalidInput", {"name": "invalid_input", "value": "This is a string"}) + instantiate_input("InvalidInput", {"name": "invalid_input", "value": "This is a string"}) def test_handle_input_valid(): @@ -218,8 +218,8 @@ def test_instantiate_input_comprehensive(): } for input_type, data in valid_data.items(): - input_instance = _instantiate_input(input_type, data) + input_instance = instantiate_input(input_type, data) assert isinstance(input_instance, InputTypesMap[input_type]) with pytest.raises(ValueError): - _instantiate_input("InvalidInput", {"name": "invalid_input", "value": "Invalid"}) + instantiate_input("InvalidInput", {"name": "invalid_input", "value": "Invalid"}) From 8415b89babdf360fbfdfeb20956b7fecda8ea275 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 9 Aug 2024 14:06:41 -0300 Subject: [PATCH 17/38] ci: integrate retry mechanism in Playwright tests to improve stability and handle potential failures (#3267) fix: integrate retry mechanism in Playwright tests to improve stability and handle potential failures --- .github/workflows/typescript_test.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/typescript_test.yml b/.github/workflows/typescript_test.yml index 901662c7303c..40ceb5119f29 100644 --- a/.github/workflows/typescript_test.yml +++ b/.github/workflows/typescript_test.yml @@ -132,10 +132,13 @@ jobs: echo "${{ secrets.ENV_VARS }}" > .env - name: Run Playwright Tests - run: | - cd src/frontend - npx playwright test --trace on --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --workers 2 - timeout-minutes: 12 + uses: nick-fields/retry@v3 + with: + timeout_minutes: 12 + max_attempts: 2 + command: | + cd src/frontend + npx playwright test --trace on --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --workers 2 - name: Upload blob report to GitHub Actions Artifacts if: always() From 07615fed1b06a93869dc419012dac3a56c23900c Mon Sep 17 00:00:00 2001 From: Scott Regan Date: Fri, 9 Aug 2024 11:23:30 -0700 Subject: [PATCH 18/38] docs: minor changes on readme text (#3270) minor changes on readme text --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7aa3e8c90c11..dbbd0f410995 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,13 @@ # [![Langflow](./docs/static/img/hero.png)](https://www.langflow.org)

- Langflow is a low-code app builder for RAG (retrieval augmented generation) and multi-agent AI applications. It’s Python-based and agnostic to any model, API, data source or database. + Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database.

@@ -27,31 +26,32 @@ https://github.com/user-attachments/assets/a1a36011-6169-4804-87ad-cfd4c5a79872

-# Core features -1. Python-based and agnostic to models, APIs, data sources, or databases. -2. Visual IDE for drag-and-drop building and testing of workflows. -3. Playground to immediately test and iterate workflows with step-by-step control. -4. Multi-agent orchestration and conversation management and retrieval. -5. Free cloud service - get started in minutes with no setup. -6. Publish workflows as back-end APIs or export as a Python application. -7. Real time observability with LangSmith, LangFuse, or LangWatch integration. -8. Support enterprise security and scale with free DataStax Langflow cloud service. -9. Prebuilt ecosystem integrations to any model, API or database. +# 🔧 Core features +1. **Python-based** and agnostic to models, APIs, data sources, or databases. +2. **Visual IDE** for drag-and-drop building and testing of workflows. +3. **Playground** to immediately test and iterate workflows with step-by-step control. +4. **Multi-agent** orchestration and conversation management and retrieval. +5. **Free cloud service** to get started in minutes with no setup. +6. **Publish as an API** or export as a Python application. +7. **Observability** with LangSmith, LangFuse, or LangWatch integration. +8. **Enterprise-grade** security and scalability with free DataStax Langflow cloud service. +9. **Customize workflows** or create flows entirely just using Python. +10. **Ecosystem integrations** as reusable components for any model, API or database. ![Integrations](https://github.com/user-attachments/assets/df4a6714-60de-4a8b-aff0-982c5aa467e3) -# Stay up-to-date +# 📅 Stay up-to-date Star Langflow on GitHub to be instantly notified of new releases. ![Star Langflow](https://github.com/user-attachments/assets/03168b17-a11d-4b2a-b0f7-c1cce69e5a2c) -# 📦 Quick Start +# 📦 Quickstart - **Install Langflow with pip** (Python version 3.10 or greater): ```shell -python -m pip install langflow -U +pip install langflow -U ``` - **Cloud:** DataStax Langflow is a hosted environment with zero setup. [Sign up for a free account.](http://langflow.datastax.com) From 583bc7401904aa30da0545c698c0ec5627ecb15f Mon Sep 17 00:00:00 2001 From: Mike Manh Date: Fri, 9 Aug 2024 18:53:39 -0400 Subject: [PATCH 19/38] fix(get-python-api-code):fixing errors in a file uploading flow, as in issue #2799 (#2815) fixing errors in a file uploading flow, as in issue #2799 Co-authored-by: Gabriel Luiz Freitas Almeida --- src/backend/base/langflow/load/utils.py | 4 ++-- .../src/modals/apiModal/utils/get-python-api-code.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/load/utils.py b/src/backend/base/langflow/load/utils.py index 8050ba51eb71..11c04c6c4d3e 100644 --- a/src/backend/base/langflow/load/utils.py +++ b/src/backend/base/langflow/load/utils.py @@ -22,7 +22,7 @@ def upload(file_path, host, flow_id): url = f"{host}/api/v1/upload/{flow_id}" with open(file_path, "rb") as file: response = httpx.post(url, files={"file": file}) - if response.status_code == 200: + if response.status_code == 200 or response.status_code == 201: return response.json() else: raise Exception(f"Error uploading file: {response.status_code}") @@ -55,7 +55,7 @@ def upload_file(file_path: str, host: str, flow_id: str, components: list[str], if response["file_path"]: for component in components: if isinstance(component, str): - tweaks[component] = {"file_path": response["file_path"]} + tweaks[component] = {"path": response["file_path"]} else: raise ValueError(f"Component ID or name must be a string. Got {type(component)}") return tweaks diff --git a/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx b/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx index 4ac41b2359b0..3482e8b0e3dd 100644 --- a/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx +++ b/src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx @@ -95,7 +95,7 @@ def main(): raise ImportError("Langflow is not installed. Please install it to use the upload_file function.") elif not args.components: raise ValueError("You need to provide the components to upload the file to.") - tweaks = upload_file(file_path=args.upload_file, host=BASE_API_URL, flow_id=ENDPOINT, components=args.components, tweaks=tweaks) + tweaks = upload_file(file_path=args.upload_file, host=BASE_API_URL, flow_id=args.endpoint, components=[args.components], tweaks=tweaks) response = run_flow( message=args.message, From 1789e2da63afdee5df096d752bf8dbdbb3dfb2f6 Mon Sep 17 00:00:00 2001 From: neeraj gupta Date: Sun, 11 Aug 2024 16:52:45 +0530 Subject: [PATCH 20/38] fix: correct issue with The "No suitable component" error is appearing on the OpenAI component and other components due to a missing check (#3279) --- .../GenericNode/components/NodeInputField/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx index 061a5bcce978..b5e4c73ea0f6 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx @@ -137,8 +137,9 @@ export default function NodeInputField({ )}
- - {displayHandle && ( + {(Array.isArray(displayHandle) + ? displayHandle.length > 0 + : displayHandle) && ( Date: Sun, 11 Aug 2024 14:48:21 -0300 Subject: [PATCH 21/38] Update README.md (#3282) --- README.md | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index dbbd0f410995..dc9b2e8925c2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@

Docs - - Free Cloud Service + Free Cloud Service - + Self Managed

@@ -20,13 +21,7 @@ README in KOREAN -

- -https://github.com/user-attachments/assets/a1a36011-6169-4804-87ad-cfd4c5a79872 - -

- -# 🔧 Core features +## ✨ Core features 1. **Python-based** and agnostic to models, APIs, data sources, or databases. 2. **Visual IDE** for drag-and-drop building and testing of workflows. 3. **Playground** to immediately test and iterate workflows with step-by-step control. @@ -41,35 +36,33 @@ https://github.com/user-attachments/assets/a1a36011-6169-4804-87ad-cfd4c5a79872 ![Integrations](https://github.com/user-attachments/assets/df4a6714-60de-4a8b-aff0-982c5aa467e3) -# 📅 Stay up-to-date +## ⭐ Stay up-to-date Star Langflow on GitHub to be instantly notified of new releases. ![Star Langflow](https://github.com/user-attachments/assets/03168b17-a11d-4b2a-b0f7-c1cce69e5a2c) -# 📦 Quickstart +## 📦 Quickstart -- **Install Langflow with pip** (Python version 3.10 or greater): +- **Install with pip** (Python 3.10 or greater): ```shell -pip install langflow -U +pip install langflow ``` - **Cloud:** DataStax Langflow is a hosted environment with zero setup. [Sign up for a free account.](http://langflow.datastax.com) - **Self-managed:** Run Langflow in your environment. [Install Langflow](https://docs.langflow.org/getting-started-installation) to run a local Langflow server, and then use the [Quickstart](https://docs.langflow.org/getting-started-quickstart) guide to create and execute a flow. - **Hugging Face:** [Clone the space using this link](https://huggingface.co/spaces/Langflow/Langflow?duplicate=true) to create a Langflow workspace. -# 👋 Contribute +[![Getting Started](https://github.com/user-attachments/assets/f1adfbe7-3c35-43a4-b265-661f3d4f875f)](https://www.youtube.com/watch?v=kinngWhaUKM) -We welcome contributions from developers of all levels to our open-source project on GitHub. If you'd like to contribute, please check our [contributing guidelines](./CONTRIBUTING.md) and help make Langflow more accessible. +## 👋 Contribute + +We welcome contributions from developers of all levels. If you'd like to contribute, please check our [contributing guidelines](./CONTRIBUTING.md) and help make Langflow more accessible. --- [![Star History Chart](https://api.star-history.com/svg?repos=langflow-ai/langflow&type=Timeline)](https://star-history.com/#langflow-ai/langflow&Date) -# 🌟 Contributors +## ❤️ Contributors [![langflow contributors](https://contrib.rocks/image?repo=langflow-ai/langflow)](https://github.com/langflow-ai/langflow/graphs/contributors) - -# 📄 License - -Langflow is released under the MIT License. See the [LICENSE](LICENSE) file for details. From ecfcbdaa5a1518b6ac5be40bc2f3a8528804fd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dtalo=20Johnny?= Date: Mon, 12 Aug 2024 13:15:57 -0300 Subject: [PATCH 22/38] docs: variable global precautions (#3285) docs: add section with variable global precautions --- .../Settings/settings-global-variables.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/docs/Settings/settings-global-variables.md b/docs/docs/Settings/settings-global-variables.md index f6fb074e19ad..f8e9af028b84 100644 --- a/docs/docs/Settings/settings-global-variables.md +++ b/docs/docs/Settings/settings-global-variables.md @@ -111,3 +111,53 @@ The default list of variables includes the ones below and more: + +### Precautions + +Global variables are stored in the database, and their values are protected by encryption using a secret +key. To preserve access to your global variables and avoid losing them, you should take a few precautions: + +1. Keep your secret key safe: Even if your database is secure, it won’t be of much use if you can't decrypt +the values. Ideally, you can set your own secret key using the `LANGFLOW_SECRET_KEY` environment variable. If +you don't provide a custom value for the secret key, one will be generated randomly and saved in the Langflow +installation directory. + +2. We use SQLite as the default database, and Langflow saves the database file in the installation directory. +To ensure the security of your data, it’s a good practice to regularly back up this file. If needed, you can +also change the database location by setting the `LANGFLOW_SAVE_DB_IN_CONFIG_DIR` environment variable to true +and configuring `LANGFLOW_CONFIG_DIR` to point to a directory of your choice. Alternatively, you can opt to use +an external database such as PostgreSQL, in which case these configurations are no longer necessary. + +For your convenience, if you’re running Langflow directly on your system or in a virtual environment +via a pip installation, you can set these values by providing Langflow with a .env file containing these +environment variables, using the following command: + +```bash +langflow run --env-file .env +``` + +If you’re running Langflow in a Docker container, you can set these values by providing Langflow with: + +```bash +docker run \ + --privileged \ + --user 1000:0 \ + -p 7860:7860 \ + -e LANGFLOW_SECRET_KEY= \ + -e LANGFLOW_SAVE_DB_IN_CONFIG_DIR=true \ + -e LANGFLOW_CONFIG_DIR=/app/container_path \ + -v $(PWD)/your_path:/app/container_path \ + langflowai/langflow:latest +``` + +or + +```bash +docker run \ + --privileged \ + --user 1000:0 \ + -p 7860:7860 \ + --env-file .env \ + -v $(PWD)/your_path:/app/container_path \ + langflowai/langflow:latest +``` From 9d8009f2f5c5e3fd3bf47760debc787deb454b1a Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Mon, 12 Aug 2024 14:57:03 -0400 Subject: [PATCH 23/38] feat: Gmail Loader component created (#3241) * feat: Gmail Loader component created * [autofix.ci] apply automated fixes * Updated Gmail.py ran make format and ran make lint * [autofix.ci] apply automated fixes * Update Gmail.py Errors Resolved. Suggestion from @ogabrielluiz added. Note this is a ChatLoader, since GmailLoader uses the BaseChatloader as base class. But this would be a highly useful feature. if required i could rewrite the component as document loader. but either way this would be an amazing feature. Planning to publish gmails tools also in next steps mostly. * solved lint issues in Gmail.py Key Changes: 1. Added Optional to the import statement. 2. Updated the type hint of label_ids to Optional[List[str]] to allow it to be None or a list of strings. * remove commented * remove commented --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jordan Frazier --- .../base/langflow/components/data/Gmail.py | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/backend/base/langflow/components/data/Gmail.py diff --git a/src/backend/base/langflow/components/data/Gmail.py b/src/backend/base/langflow/components/data/Gmail.py new file mode 100644 index 000000000000..6832bc9eec6b --- /dev/null +++ b/src/backend/base/langflow/components/data/Gmail.py @@ -0,0 +1,190 @@ +import base64 +import re +import json +from typing import Any, Iterator, List, Optional +from google.oauth2.credentials import Credentials +from googleapiclient.discovery import build +from langflow.custom import Component +from langflow.inputs import MessageTextInput +from langflow.io import SecretStrInput +from langflow.template import Output +from langflow.schema import Data +from langchain_google_community.gmail.loader import GMailLoader +from langchain_core.chat_sessions import ChatSession +from langchain_core.messages import HumanMessage +from json.decoder import JSONDecodeError +from google.auth.exceptions import RefreshError + + +class GmailLoaderComponent(Component): + display_name = "Gmail Loader" + description = "Loads emails from Gmail using provided credentials." + icon = "Google" + + inputs = [ + SecretStrInput( + name="json_string", + display_name="JSON String of the Service Account Token", + info="JSON string containing OAuth 2.0 access token information for service account access", + required=True, + value=str("""{ + "account": "", + "client_id": "", + "client_secret": "", + "expiry": "", + "refresh_token": "", + "scopes": [ + "https://www.googleapis.com/auth/gmail.readonly", + ], + "token": "", + "token_uri": "https://oauth2.googleapis.com/token", + "universe_domain": "googleapis.com" + }"""), + ), + MessageTextInput( + name="label_ids", + display_name="Label IDs", + info="Comma-separated list of label IDs to filter emails.", + required=True, + value="INBOX,SENT,UNREAD,IMPORTANT", + ), + MessageTextInput( + name="max_results", + display_name="Max Results", + info="Maximum number of emails to load.", + required=True, + value="10", + ), + ] + + outputs = [ + Output(display_name="Data", name="data", method="load_emails"), + ] + + def load_emails(self) -> Data: + class CustomGMailLoader(GMailLoader): + def __init__( + self, creds: Any, n: int = 100, label_ids: Optional[List[str]] = None, raise_error: bool = False + ) -> None: + super().__init__(creds, n, raise_error) + self.label_ids = label_ids if label_ids is not None else ["SENT"] + + def clean_message_content(self, message): + # Remove URLs + message = re.sub(r"http\S+|www\S+|https\S+", "", message, flags=re.MULTILINE) + + # Remove email addresses + message = re.sub(r"\S+@\S+", "", message) + + # Remove special characters and excessive whitespace + message = re.sub(r"[^A-Za-z0-9\s]+", " ", message) + message = re.sub(r"\s{2,}", " ", message) + + # Trim leading and trailing whitespace + message = message.strip() + + return message + + def _extract_email_content(self, msg: Any) -> HumanMessage: + from_email = None + for values in msg["payload"]["headers"]: + name = values["name"] + if name == "From": + from_email = values["value"] + if from_email is None: + raise ValueError("From email not found.") + + if "parts" in msg["payload"]: + parts = msg["payload"]["parts"] + else: + parts = [msg["payload"]] + + for part in parts: + if part["mimeType"] == "text/plain": + data = part["body"]["data"] + data = base64.urlsafe_b64decode(data).decode("utf-8") + pattern = re.compile(r"\r\nOn .+(\r\n)*wrote:\r\n") + newest_response = re.split(pattern, data)[0] + message = HumanMessage( + content=self.clean_message_content(newest_response), + additional_kwargs={"sender": from_email}, + ) + return message + raise ValueError("No plain text part found in the email.") + + def _get_message_data(self, service: Any, message: Any) -> ChatSession: + msg = service.users().messages().get(userId="me", id=message["id"]).execute() + message_content = self._extract_email_content(msg) + + in_reply_to = None + email_data = msg["payload"]["headers"] + for values in email_data: + name = values["name"] + if name == "In-Reply-To": + in_reply_to = values["value"] + + thread_id = msg["threadId"] + + if in_reply_to: + thread = service.users().threads().get(userId="me", id=thread_id).execute() + messages = thread["messages"] + + response_email = None + for message in messages: + email_data = message["payload"]["headers"] + for values in email_data: + if values["name"] == "Message-ID": + message_id = values["value"] + if message_id == in_reply_to: + response_email = message + if response_email is None: + raise ValueError("Response email not found in the thread.") + starter_content = self._extract_email_content(response_email) + return ChatSession(messages=[starter_content, message_content]) + else: + return ChatSession(messages=[message_content]) + + def lazy_load(self) -> Iterator[ChatSession]: + service = build("gmail", "v1", credentials=self.creds) + results = ( + service.users().messages().list(userId="me", labelIds=self.label_ids, maxResults=self.n).execute() + ) + messages = results.get("messages", []) + if not messages: + print("No messages found with the specified labels.") + for message in messages: + try: + yield self._get_message_data(service, message) + except Exception as e: + if self.raise_error: + raise e + else: + print(f"Error processing message {message['id']}: {e}") + + json_string = self.json_string + label_ids = self.label_ids.split(",") if self.label_ids else ["INBOX"] + max_results = int(self.max_results) if self.max_results else 100 + + # Load the token information from the JSON string + try: + token_info = json.loads(json_string) + except JSONDecodeError as e: + raise ValueError("Invalid JSON string") from e + + creds = Credentials.from_authorized_user_info(token_info) + + # Initialize the custom loader with the provided credentials + loader = CustomGMailLoader(creds=creds, n=max_results, label_ids=label_ids) + + try: + docs = loader.load() + except RefreshError as e: + raise ValueError( + "Authentication error: Unable to refresh authentication token. Please try to reauthenticate." + ) from e + except Exception as e: + raise ValueError(f"Error loading documents: {e}") from e + + # Return the loaded documents + self.status = docs + return Data(data={"text": docs}) From 775d659380911cce06e0842d796540e97d1d1d63 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 12 Aug 2024 16:21:27 -0300 Subject: [PATCH 24/38] fix: correct vertex build delete function (#3289) * refactor: Rearrange imports for clarity and consistency. * fix: delete now executes the sql statement correctly --- .../langflow/services/database/models/vertex_builds/crud.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/services/database/models/vertex_builds/crud.py b/src/backend/base/langflow/services/database/models/vertex_builds/crud.py index f846d03a009b..972b3d45d5f3 100644 --- a/src/backend/base/langflow/services/database/models/vertex_builds/crud.py +++ b/src/backend/base/langflow/services/database/models/vertex_builds/crud.py @@ -2,8 +2,7 @@ from uuid import UUID from sqlalchemy.exc import IntegrityError -from sqlmodel import Session, select, col -from sqlalchemy import delete +from sqlmodel import Session, col, delete, select from langflow.services.database.models.vertex_builds.model import VertexBuildBase, VertexBuildTable @@ -32,5 +31,5 @@ def log_vertex_build(db: Session, vertex_build: VertexBuildBase) -> VertexBuildT def delete_vertex_builds_by_flow_id(db: Session, flow_id: UUID) -> None: - delete(VertexBuildTable).where(VertexBuildTable.flow.has(id=flow_id)) # type: ignore + db.exec(delete(VertexBuildTable).where(VertexBuildTable.flow_id == flow_id)) db.commit() From e09f157af6855932c6c8524d9e7424f9c2f0cf5f Mon Sep 17 00:00:00 2001 From: William Espegren <131612909+WilliamEspegren@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:45:20 +0200 Subject: [PATCH 25/38] fix: update parameters in spider components (#3280) * fix: make spider tool work * upgrade spider-client * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- poetry.lock | 2 +- .../langchain_utilities/SpiderTool.py | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8ed44e90b426..8cd378366e1b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5236,7 +5236,7 @@ python-multipart = "^0.0.7" rich = "^13.7.0" sentry-sdk = {version = "^2.5.1", extras = ["fastapi", "loguru"]} setuptools = ">=70" -spider-client = "^0.0.27" +spider-client = "^0.0.60" sqlmodel = "^0.0.18" typer = "^0.12.0" uncurl = "^0.0.11" diff --git a/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py b/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py index ee4b732735c5..0cef340af328 100644 --- a/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py +++ b/src/backend/base/langflow/components/langchain_utilities/SpiderTool.py @@ -1,4 +1,4 @@ -from spider.spider import Spider # type: ignore +from spider.spider import Spider from langflow.base.langchain_utilities.spider_constants import MODES from langflow.custom import Component @@ -59,7 +59,7 @@ class SpiderTool(Component): advanced=True, ), BoolInput( - name="use_readability", + name="readability", display_name="Use Readability", info="Use readability to pre-process the content for reading.", advanced=True, @@ -89,15 +89,15 @@ class SpiderTool(Component): def crawl(self) -> list[Data]: if self.params: - parameters = self.params.data + parameters = self.params["data"] else: parameters = { - "limit": self.limit, - "depth": self.depth, - "blacklist": self.blacklist, - "whitelist": self.whitelist, - "use_readability": self.use_readability, - "request_timeout": self.request_timeout, + "limit": self.limit if self.limit else None, + "depth": self.depth if self.depth else None, + "blacklist": self.blacklist if self.blacklist else None, + "whitelist": self.whitelist if self.whitelist else None, + "readability": self.readability, + "request_timeout": self.request_timeout if self.request_timeout else None, "metadata": self.metadata, "return_format": "markdown", } @@ -117,5 +117,10 @@ def crawl(self) -> list[Data]: records = [] for record in result: - records.append(Data(data={"content": record["content"], "url": record["url"]})) + if self.metadata: + records.append( + Data(data={"content": record["content"], "url": record["url"], "metadata": record["metadata"]}) + ) + else: + records.append(Data(data={"content": record["content"], "url": record["url"]})) return records From a00bbe0a84afae62f3af3c51fa995b11da555bac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:53:07 +0000 Subject: [PATCH 26/38] build(deps): bump aiohttp from 3.10.1 to 3.10.2 in /src/backend/base (#3269) Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.1 to 3.10.2. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.1...v3.10.2) --- updated-dependencies: - dependency-name: aiohttp dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/backend/base/poetry.lock | 154 +++++++++++++++++------------------ 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/src/backend/base/poetry.lock b/src/backend/base/poetry.lock index 8deb46e0bfab..5859cf5d0ffa 100644 --- a/src/backend/base/poetry.lock +++ b/src/backend/base/poetry.lock @@ -24,87 +24,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.1" +version = "3.10.2" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:47b4c2412960e64d97258f40616efddaebcb34ff664c8a972119ed38fac2a62c"}, - {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7dbf637f87dd315fa1f36aaed8afa929ee2c607454fb7791e74c88a0d94da59"}, - {file = "aiohttp-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c8fb76214b5b739ce59e2236a6489d9dc3483649cfd6f563dbf5d8e40dbdd57d"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c577cdcf8f92862363b3d598d971c6a84ed8f0bf824d4cc1ce70c2fb02acb4a"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:777e23609899cb230ad2642b4bdf1008890f84968be78de29099a8a86f10b261"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b07286a1090483799599a2f72f76ac396993da31f6e08efedb59f40876c144fa"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9db600a86414a9a653e3c1c7f6a2f6a1894ab8f83d11505247bd1b90ad57157"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c3f1eb280008e51965a8d160a108c333136f4a39d46f516c64d2aa2e6a53f2"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f5dd109a925fee4c9ac3f6a094900461a2712df41745f5d04782ebcbe6479ccb"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8c81ff4afffef9b1186639506d70ea90888218f5ddfff03870e74ec80bb59970"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2a384dfbe8bfebd203b778a30a712886d147c61943675f4719b56725a8bbe803"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b9fb6508893dc31cfcbb8191ef35abd79751db1d6871b3e2caee83959b4d91eb"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:88596384c3bec644a96ae46287bb646d6a23fa6014afe3799156aef42669c6bd"}, - {file = "aiohttp-3.10.1-cp310-cp310-win32.whl", hash = "sha256:68164d43c580c2e8bf8e0eb4960142919d304052ccab92be10250a3a33b53268"}, - {file = "aiohttp-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:d6bbe2c90c10382ca96df33b56e2060404a4f0f88673e1e84b44c8952517e5f3"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6979b4f20d3e557a867da9d9227de4c156fcdcb348a5848e3e6190fd7feb972"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03c0c380c83f8a8d4416224aafb88d378376d6f4cadebb56b060688251055cd4"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c2b104e81b3c3deba7e6f5bc1a9a0e9161c380530479970766a6655b8b77c7c"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b023b68c61ab0cd48bd38416b421464a62c381e32b9dc7b4bdfa2905807452a4"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a07c76a82390506ca0eabf57c0540cf5a60c993c442928fe4928472c4c6e5e6"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:41d8dab8c64ded1edf117d2a64f353efa096c52b853ef461aebd49abae979f16"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:615348fab1a9ef7d0960a905e83ad39051ae9cb0d2837da739b5d3a7671e497a"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:256ee6044214ee9d66d531bb374f065ee94e60667d6bbeaa25ca111fc3997158"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d5bb926805022508b7ddeaad957f1fce7a8d77532068d7bdb431056dc630cd"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:028faf71b338f069077af6315ad54281612705d68889f5d914318cbc2aab0d50"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5c12310d153b27aa630750be44e79313acc4e864c421eb7d2bc6fa3429c41bf8"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:de1a91d5faded9054957ed0a9e01b9d632109341942fc123947ced358c5d9009"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9c186b270979fb1dee3ababe2d12fb243ed7da08b30abc83ebac3a928a4ddb15"}, - {file = "aiohttp-3.10.1-cp311-cp311-win32.whl", hash = "sha256:4a9ce70f5e00380377aac0e568abd075266ff992be2e271765f7b35d228a990c"}, - {file = "aiohttp-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:a77c79bac8d908d839d32c212aef2354d2246eb9deb3e2cb01ffa83fb7a6ea5d"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2212296cdb63b092e295c3e4b4b442e7b7eb41e8a30d0f53c16d5962efed395d"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4dcb127ca3eb0a61205818a606393cbb60d93b7afb9accd2fd1e9081cc533144"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb8b79a65332e1a426ccb6290ce0409e1dc16b4daac1cc5761e059127fa3d134"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc24f707ed9cb961f6ee04020ca01de2c89b2811f3cf3361dc7c96a14bfbcc"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cb54f5725b4b37af12edf6c9e834df59258c82c15a244daa521a065fbb11717"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d03e948e53b3639ce4d438f3d1d8202898ec6655cadcc09ec99229d4adc2a9"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786299d719eb5d868f161aeec56d589396b053925b7e0ce36e983d30d0a3e55c"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abda4009a30d51d3f06f36bc7411a62b3e647fa6cc935ef667e3e3d3a7dd09b1"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67f7639424c313125213954e93a6229d3a1d386855d70c292a12628f600c7150"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e5a26d7aac4c0d8414a347da162696eea0629fdce939ada6aedf951abb1d745"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:120548d89f14b76a041088b582454d89389370632ee12bf39d919cc5c561d1ca"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f5293726943bdcea24715b121d8c4ae12581441d22623b0e6ab12d07ce85f9c4"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f8605e573ed6c44ec689d94544b2c4bb1390aaa723a8b5a2cc0a5a485987a68"}, - {file = "aiohttp-3.10.1-cp312-cp312-win32.whl", hash = "sha256:e7168782621be4448d90169a60c8b37e9b0926b3b79b6097bc180c0a8a119e73"}, - {file = "aiohttp-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fbf8c0ded367c5c8eaf585f85ca8dd85ff4d5b73fb8fe1e6ac9e1b5e62e11f7"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:54b7f4a20d7cc6bfa4438abbde069d417bb7a119f870975f78a2b99890226d55"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fa643ca990323db68911b92f3f7a0ca9ae300ae340d0235de87c523601e58d9"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8311d0d690487359fe2247ec5d2cac9946e70d50dced8c01ce9e72341c21151"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222821c60b8f6a64c5908cb43d69c0ee978a1188f6a8433d4757d39231b42cdb"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7b55d9ede66af7feb6de87ff277e0ccf6d51c7db74cc39337fe3a0e31b5872d"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a95151a5567b3b00368e99e9c5334a919514f60888a6b6d2054fea5e66e527e"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9e9171d2fe6bfd9d3838a6fe63b1e91b55e0bf726c16edf265536e4eafed19"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a57e73f9523e980f6101dc9a83adcd7ac0006ea8bf7937ca3870391c7bb4f8ff"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0df51a3d70a2bfbb9c921619f68d6d02591f24f10e9c76de6f3388c89ed01de6"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:b0de63ff0307eac3961b4af74382d30220d4813f36b7aaaf57f063a1243b4214"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8db9b749f589b5af8e4993623dbda6716b2b7a5fcb0fa2277bf3ce4b278c7059"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6b14c19172eb53b63931d3e62a9749d6519f7c121149493e6eefca055fcdb352"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cd57ad998e3038aa87c38fe85c99ed728001bf5dde8eca121cadee06ee3f637"}, - {file = "aiohttp-3.10.1-cp38-cp38-win32.whl", hash = "sha256:df31641e3f02b77eb3c5fb63c0508bee0fc067cf153da0e002ebbb0db0b6d91a"}, - {file = "aiohttp-3.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:93094eba50bc2ad4c40ff4997ead1fdcd41536116f2e7d6cfec9596a8ecb3615"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:440954ddc6b77257e67170d57b1026aa9545275c33312357472504eef7b4cc0b"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9f8beed277488a52ee2b459b23c4135e54d6a819eaba2e120e57311015b58e9"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8a8221a63602008550022aa3a4152ca357e1dde7ab3dd1da7e1925050b56863"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a702bd3663b5cbf3916e84bf332400d24cdb18399f0877ca6b313ce6c08bfb43"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1988b370536eb14f0ce7f3a4a5b422ab64c4e255b3f5d7752c5f583dc8c967fc"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ccf1f0a304352c891d124ac1a9dea59b14b2abed1704aaa7689fc90ef9c5be1"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc3ea6ef2a83edad84bbdb5d96e22f587b67c68922cd7b6f9d8f24865e655bcf"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b47c125ab07f0831803b88aeb12b04c564d5f07a1c1a225d4eb4d2f26e8b5e"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21778552ef3d44aac3278cc6f6d13a6423504fa5f09f2df34bfe489ed9ded7f5"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bde0693073fd5e542e46ea100aa6c1a5d36282dbdbad85b1c3365d5421490a92"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bf66149bb348d8e713f3a8e0b4f5b952094c2948c408e1cfef03b49e86745d60"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:587237571a85716d6f71f60d103416c9df7d5acb55d96d3d3ced65f39bff9c0c"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bfe33cba6e127d0b5b417623c9aa621f0a69f304742acdca929a9fdab4593693"}, - {file = "aiohttp-3.10.1-cp39-cp39-win32.whl", hash = "sha256:9fbff00646cf8211b330690eb2fd64b23e1ce5b63a342436c1d1d6951d53d8dd"}, - {file = "aiohttp-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:5951c328f9ac42d7bce7a6ded535879bc9ae13032818d036749631fa27777905"}, - {file = "aiohttp-3.10.1.tar.gz", hash = "sha256:8b0d058e4e425d3b45e8ec70d49b402f4d6b21041e674798b1f91ba027c73f28"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95213b3d79c7e387144e9cb7b9d2809092d6ff2c044cb59033aedc612f38fb6d"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1aa005f060aff7124cfadaa2493f00a4e28ed41b232add5869e129a2e395935a"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eabe6bf4c199687592f5de4ccd383945f485779c7ffb62a9b9f1f8a3f9756df8"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e010736fc16d21125c7e2dc5c350cd43c528b85085c04bf73a77be328fe944"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99f81f9c1529fd8e03be4a7bd7df32d14b4f856e90ef6e9cbad3415dbfa9166c"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d611d1a01c25277bcdea06879afbc11472e33ce842322496b211319aa95441bb"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00191d38156e09e8c81ef3d75c0d70d4f209b8381e71622165f22ef7da6f101"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74c091a5ded6cb81785de2d7a8ab703731f26de910dbe0f3934eabef4ae417cc"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:18186a80ec5a701816adbf1d779926e1069392cf18504528d6e52e14b5920525"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5a7ceb2a0d2280f23a02c64cd0afdc922079bb950400c3dd13a1ab2988428aac"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8bd7be6ff6c162a60cb8fce65ee879a684fbb63d5466aba3fa5b9288eb04aefa"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fae962b62944eaebff4f4fddcf1a69de919e7b967136a318533d82d93c3c6bd1"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a0fde16d284efcacbe15fb0c1013f0967b6c3e379649239d783868230bf1db42"}, + {file = "aiohttp-3.10.2-cp310-cp310-win32.whl", hash = "sha256:f81cd85a0e76ec7b8e2b6636fe02952d35befda4196b8c88f3cec5b4fb512839"}, + {file = "aiohttp-3.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:54ba10eb5a3481c28282eb6afb5f709aedf53cf9c3a31875ffbdc9fc719ffd67"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87fab7f948e407444c2f57088286e00e2ed0003ceaf3d8f8cc0f60544ba61d91"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6ad66ed660d46503243cbec7b2b3d8ddfa020f984209b3b8ef7d98ce69c3f2"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a4be88807283bd96ae7b8e401abde4ca0bab597ba73b5e9a2d98f36d451e9aac"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01c98041f90927c2cbd72c22a164bb816fa3010a047d264969cf82e1d4bcf8d1"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54e36c67e1a9273ecafab18d6693da0fb5ac48fd48417e4548ac24a918c20998"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7de3ddb6f424af54535424082a1b5d1ae8caf8256ebd445be68c31c662354720"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dd9c7db94b4692b827ce51dcee597d61a0e4f4661162424faf65106775b40e7"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e57e21e1167705f8482ca29cc5d02702208d8bf4aff58f766d94bcd6ead838cd"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a1a50e59b720060c29e2951fd9f13c01e1ea9492e5a527b92cfe04dd64453c16"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:686c87782481fda5ee6ba572d912a5c26d9f98cc5c243ebd03f95222af3f1b0f"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:dafb4abb257c0ed56dc36f4e928a7341b34b1379bd87e5a15ce5d883c2c90574"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:494a6f77560e02bd7d1ab579fdf8192390567fc96a603f21370f6e63690b7f3d"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6fe8503b1b917508cc68bf44dae28823ac05e9f091021e0c41f806ebbb23f92f"}, + {file = "aiohttp-3.10.2-cp311-cp311-win32.whl", hash = "sha256:4ddb43d06ce786221c0dfd3c91b4892c318eaa36b903f7c4278e7e2fa0dd5102"}, + {file = "aiohttp-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:ca2f5abcb0a9a47e56bac173c01e9f6c6e7f27534d91451c5f22e6a35a5a2093"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:14eb6b17f6246959fb0b035d4f4ae52caa870c4edfb6170aad14c0de5bfbf478"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:465e445ec348d4e4bd349edd8b22db75f025da9d7b6dc1369c48e7935b85581e"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:341f8ece0276a828d95b70cd265d20e257f5132b46bf77d759d7f4e0443f2906"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01fbb87b5426381cd9418b3ddcf4fc107e296fa2d3446c18ce6c76642f340a3"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c474af073e1a6763e1c5522bbb2d85ff8318197e4c6c919b8d7886e16213345"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d9076810a5621236e29b2204e67a68e1fe317c8727ee4c9abbfbb1083b442c38"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8f515d6859e673940e08de3922b9c4a2249653b0ac181169313bd6e4b1978ac"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:655e583afc639bef06f3b2446972c1726007a21003cd0ef57116a123e44601bc"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8da9449a575133828cc99985536552ea2dcd690e848f9d41b48d8853a149a959"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19073d57d0feb1865d12361e2a1f5a49cb764bf81a4024a3b608ab521568093a"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8e98e1845805f184d91fda6f9ab93d7c7b0dddf1c07e0255924bfdb151a8d05"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:377220a5efde6f9497c5b74649b8c261d3cce8a84cb661be2ed8099a2196400a"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92f7f4a4dc9cdb5980973a74d43cdbb16286dacf8d1896b6c3023b8ba8436f8e"}, + {file = "aiohttp-3.10.2-cp312-cp312-win32.whl", hash = "sha256:9bb2834a6f11d65374ce97d366d6311a9155ef92c4f0cee543b2155d06dc921f"}, + {file = "aiohttp-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:518dc3cb37365255708283d1c1c54485bbacccd84f0a0fb87ed8917ba45eda5b"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f98e70bbbf693086efe4b86d381efad8edac040b8ad02821453083d15ec315f"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f6f0b252a009e98fe84028a4ec48396a948e7a65b8be06ccfc6ef68cf1f614d"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9360e3ffc7b23565600e729e8c639c3c50d5520e05fdf94aa2bd859eef12c407"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3988044d1635c7821dd44f0edfbe47e9875427464e59d548aece447f8c22800a"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a9d59da1543a6f1478c3436fd49ec59be3868bca561a33778b4391005e499d"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f49bdb94809ac56e09a310a62f33e5f22973d6fd351aac72a39cd551e98194"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddfd2dca3f11c365d6857a07e7d12985afc59798458a2fdb2ffa4a0332a3fd43"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c1508ec97b2cd3e120bfe309a4ff8e852e8a7460f1ef1de00c2c0ed01e33c"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:49904f38667c44c041a0b44c474b3ae36948d16a0398a8f8cd84e2bb3c42a069"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:352f3a4e5f11f3241a49b6a48bc5b935fabc35d1165fa0d87f3ca99c1fcca98b"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:fc61f39b534c5d5903490478a0dd349df397d2284a939aa3cbaa2fb7a19b8397"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:ad2274e707be37420d0b6c3d26a8115295fe9d8e6e530fa6a42487a8ca3ad052"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c836bf3c7512100219fe1123743fd8dd9a2b50dd7cfb0c3bb10d041309acab4b"}, + {file = "aiohttp-3.10.2-cp38-cp38-win32.whl", hash = "sha256:53e8898adda402be03ff164b0878abe2d884e3ea03a4701e6ad55399d84b92dc"}, + {file = "aiohttp-3.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:7cc8f65f5b22304693de05a245b6736b14cb5bc9c8a03da6e2ae9ef15f8b458f"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9dfc906d656e14004c5bc672399c1cccc10db38df2b62a13fb2b6e165a81c316"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:91b10208b222ddf655c3a3d5b727879d7163db12b634492df41a9182a76edaae"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fd16b5e1a7bdd14668cd6bde60a2a29b49147a535c74f50d8177d11b38433a7"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2bfdda4971bd79201f59adbad24ec2728875237e1c83bba5221284dbbf57bda"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69d73f869cf29e8a373127fc378014e2b17bcfbe8d89134bc6fb06a2f67f3cb3"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df59f8486507c421c0620a2c3dce81fbf1d54018dc20ff4fecdb2c106d6e6abc"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df930015db36b460aa9badbf35eccbc383f00d52d4b6f3de2ccb57d064a6ade"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:562b1153ab7f766ee6b8b357ec777a302770ad017cf18505d34f1c088fccc448"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d984db6d855de58e0fde1ef908d48fe9a634cadb3cf715962722b4da1c40619d"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:14dc3fcb0d877911d775d511eb617a486a8c48afca0a887276e63db04d3ee920"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b52a27a5c97275e254704e1049f4b96a81e67d6205f52fa37a4777d55b0e98ef"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:cd33d9de8cfd006a0d0fe85f49b4183c57e91d18ffb7e9004ce855e81928f704"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1238fc979160bc03a92fff9ad021375ff1c8799c6aacb0d8ea1b357ea40932bb"}, + {file = "aiohttp-3.10.2-cp39-cp39-win32.whl", hash = "sha256:e2f43d238eae4f0b04f58d4c0df4615697d4ca3e9f9b1963d49555a94f0f5a04"}, + {file = "aiohttp-3.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:947847f07a8f81d7b39b2d0202fd73e61962ebe17ac2d8566f260679e467da7b"}, + {file = "aiohttp-3.10.2.tar.gz", hash = "sha256:4d1f694b5d6e459352e5e925a42e05bac66655bfde44d81c59992463d2897014"}, ] [package.dependencies] From 7a93a708ee2ff0786a7c7e3220d1c30b96fc78f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dtalo=20Johnny?= Date: Mon, 12 Aug 2024 19:09:08 -0300 Subject: [PATCH 27/38] fix: message sorting in playground (#3293) --- .../services/database/models/vertex_builds/model.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/services/database/models/vertex_builds/model.py b/src/backend/base/langflow/services/database/models/vertex_builds/model.py index 78d582fe8587..e45a659a39f8 100644 --- a/src/backend/base/langflow/services/database/models/vertex_builds/model.py +++ b/src/backend/base/langflow/services/database/models/vertex_builds/model.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Optional from uuid import UUID, uuid4 -from pydantic import field_validator, BaseModel +from pydantic import field_serializer, field_validator, BaseModel from sqlmodel import JSON, Column, Field, Relationship, SQLModel @@ -32,6 +32,13 @@ def validate_flow_id(cls, value): value = UUID(value) return value + @field_serializer("timestamp") + @classmethod + def serialize_timestamp(cls, value): + if value.tzinfo is None: + value = value.replace(tzinfo=timezone.utc) + return value + class VertexBuildTable(VertexBuildBase, table=True): # type: ignore __tablename__ = "vertex_build" From 7264028e414da4f0284e79e322cca38e384cd3a5 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:21:52 -0300 Subject: [PATCH 28/38] refactor: optimize flow saving functionality and implement manual saving (#3283) * fixed patch update flow * fixed update flow patch to receive id by payload * created save flow hook with auto save and manual save functions * fix poetry lock * added auto save check with environment variable * removed unused user * separated autosave and put the flow as a creation with nodes and edges * removed set nodes that skipped saving * implemented auto save hook * removed autosave from setNodes and setEdges * added auto save hook and saved on viewport move and added useEffect to save on nodes and edges changed * changed type of setNodes * removed unused var * removed deletion of empty flow * Added saving of flow on button when autoSave is disabled * disable saving when the nodes are empty * removed save loading as false when the access token is renewed * implemented useDebounce * added save loading to save flow hook * removed setting nodes and edges on fetching, since they are set when the current flow is updated * removed unused var * use debounce hook to save flow * set nodes and edges on current flow id change * removed useplaygroundeffect * removed unused import * put set save loading before the If * removed flow cleaning and inputs setting, since the inputs and outputs are set on the ResetFlow function * updated to use ResetFlow function to update everything regarding flow * removed flow pool get on resetFlow, for it to be fetched only if the user is inside the flow * updated packagelock * Changed router to outlet on app.tsx to use createRouter * Created authSettingsGuard to guard the general settings * Fixed routes to use createBrowserRouter to allow the use of useBlocker * Changed index.tsx to use RouterProvider and the router just created * Changed flowStore to have a local flow state * Implemented setting the current flow state when saving the flow * Added the update of current flow when auto saving * changed current flow to use the current flow from Flow Store instead of Flows Manager Store * Changed codeTabsComponent Tweaks check to show if its checked * Removed unused variables * Removed browser router from context wrapper * Removed unused console.log * Changed initialSetup to just run when opening the modal * changed confirmationModal to have destructiveCancel and to only call onCancel if the other buttons were not pressed * Created a SaveChangesModal that confirms if the user wants to save their changes * Get folder by id when folder id changes too * Changed reset flow calls to store whole flow * Added check if user is exiting page to prevent him when there are unsaved changes * Added new types on ConfirmationModalType * Implement save on clicking the save button on the header * added save component shortcut to use save shortcut as save flow * added save component shortcut on shortcutsStore type * changed save shortcut to save component on node toolbar * added save shortcut to header menubar * changed shortcuts name to be compatible with existing ones * changed shortcuts to be backwards compatible * changed save to changes to maintain retrocompatibility * changed save_component to save to maintain retrocompatibility * Changed time difference to unsaved changes * changed the toolbar select item to get the right save shortcut * Changed save flow to use current flow from useFlowStore instead of the previous saved flow * changed changesNotSaved to include flow name and metadata * Added way of saving the flow settings just locally instead of directly to database * Changed shareModal to save flow with hook * removed old auto saving on connect * Removed save functions from flowsManagerStore * refactor: Remove unused imports and state variables in EditFlowSettings component * use current flow not saved one and refactored page to not receive flow * added check of isFlowPage to display the menubar * Added checks to render playground if API key is valid and if Flows exists * Added check to not display X on chat on playground page * Updated flows variable to be undefined by start to prevent things from loading before flows initialize * Implemented log builds parameter to not allow the builds to be logged if user not on flowPage --- src/frontend/package-lock.json | 1 - src/frontend/src/App.tsx | 5 +- .../components/OutputComponent/index.tsx | 15 - .../src/CustomNodes/GenericNode/index.tsx | 1 - .../components/authSettingsGuard/index.tsx | 19 ++ .../hooks/use-playground-effect.tsx | 27 -- .../src/components/cardComponent/index.tsx | 30 +- .../src/components/chatComponent/index.tsx | 7 +- .../components/codeTabsComponent/index.tsx | 1 + .../editFlowSettingsComponent/index.tsx | 2 - .../components/menuBar/index.tsx | 82 ++++- .../src/components/headerComponent/index.tsx | 17 +- .../hooks/use-on-file-drop.tsx | 9 +- src/frontend/src/constants/constants.ts | 6 +- src/frontend/src/contexts/authContext.tsx | 9 - src/frontend/src/contexts/index.tsx | 23 +- src/frontend/src/controllers/API/api.tsx | 4 - src/frontend/src/controllers/API/index.ts | 28 -- .../API/queries/_builds/use-get-builds.ts | 3 +- .../queries/flows/use-patch-update-flow.ts | 25 +- src/frontend/src/hooks/flows/use-add-flow.ts | 9 +- .../src/hooks/flows/use-autosave-flow.ts | 19 ++ .../src/hooks/flows/use-delete-flow.ts | 2 +- src/frontend/src/hooks/flows/use-save-flow.ts | 100 ++++++ src/frontend/src/hooks/use-debounce.ts | 13 + src/frontend/src/index.tsx | 15 +- .../IOModal/components/chatView/index.tsx | 1 - src/frontend/src/modals/IOModal/index.tsx | 12 +- src/frontend/src/modals/apiModal/index.tsx | 3 - .../src/modals/confirmationModal/index.tsx | 14 +- src/frontend/src/modals/exportModal/index.tsx | 4 +- .../src/modals/flowSettingsModal/index.tsx | 59 ++-- .../src/modals/saveChangesModal/index.tsx | 22 ++ src/frontend/src/modals/shareModal/index.tsx | 7 +- src/frontend/src/pages/AdminPage/index.tsx | 9 - .../components/PageComponent/index.tsx | 80 ++--- .../extraSidebarComponent/index.tsx | 69 +--- .../sideBarDraggableComponent/index.tsx | 2 +- .../components/nodeToolbarComponent/index.tsx | 15 +- src/frontend/src/pages/FlowPage/index.tsx | 64 +++- .../components/componentsComponent/index.tsx | 2 +- .../pages/MainPage/pages/mainPage/index.tsx | 11 +- src/frontend/src/pages/Playground/index.tsx | 48 +-- .../src/pages/ProfileSettingsPage/index.tsx | 12 +- src/frontend/src/pages/SettingsPage/index.tsx | 11 - .../SettingsPage/pages/GeneralPage/index.tsx | 7 +- .../pages/hooks/use-scroll-to-element.tsx | 9 +- src/frontend/src/pages/StorePage/index.tsx | 10 +- src/frontend/src/pages/ViewPage/index.tsx | 38 ++- src/frontend/src/routes.tsx | 297 ++++++++---------- src/frontend/src/stores/darkStore.ts | 2 +- src/frontend/src/stores/flowStore.ts | 84 ++--- src/frontend/src/stores/flowsManagerStore.ts | 82 +---- src/frontend/src/stores/shortcuts.ts | 3 +- src/frontend/src/types/components/index.ts | 3 +- src/frontend/src/types/store/index.ts | 1 + src/frontend/src/types/zustand/flow/index.ts | 29 +- .../src/types/zustand/flowsManager/index.ts | 21 +- src/frontend/src/utils/buildUtils.ts | 5 + .../tests/end-to-end/flowSettings.spec.ts | 4 - .../tests/end-to-end/store-shard-2.spec.ts | 3 +- src/frontend/vite.config.mts | 3 + 62 files changed, 731 insertions(+), 787 deletions(-) create mode 100644 src/frontend/src/components/authSettingsGuard/index.tsx delete mode 100644 src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx create mode 100644 src/frontend/src/hooks/flows/use-autosave-flow.ts create mode 100644 src/frontend/src/hooks/flows/use-save-flow.ts create mode 100644 src/frontend/src/hooks/use-debounce.ts create mode 100644 src/frontend/src/modals/saveChangesModal/index.tsx diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index f150e6b429da..db0e15228cd9 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -1079,7 +1079,6 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", - "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index ddccd55cceed..9423ce55f237 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect } from "react"; import { Cookies } from "react-cookie"; import { ErrorBoundary } from "react-error-boundary"; +import { Outlet } from "react-router-dom"; import "reactflow/dist/style.css"; import "./App.css"; import AlertDisplayArea from "./alerts/displayArea"; @@ -22,7 +23,6 @@ import { useGetHealthQuery } from "./controllers/API/queries/health"; import { useGetVersionQuery } from "./controllers/API/queries/version"; import { setupAxiosDefaults } from "./controllers/API/utils"; import useTrackLastVisitedPath from "./hooks/use-track-last-visited-path"; -import Router from "./routes"; import useAlertStore from "./stores/alertStore"; import useAuthStore from "./stores/authStore"; import { useDarkStore } from "./stores/darkStore"; @@ -172,8 +172,7 @@ export default function App() { > - - +
diff --git a/src/frontend/src/CustomNodes/GenericNode/components/OutputComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/OutputComponent/index.tsx index 6c18e6b0bfad..d3992035f75c 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/OutputComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/OutputComponent/index.tsx @@ -1,17 +1,5 @@ -import { cloneDeep } from "lodash"; -import { useUpdateNodeInternals } from "reactflow"; -import ForwardedIconComponent from "../../../../components/genericIconComponent"; import ShadTooltip from "../../../../components/shadTooltipComponent"; -import { Button } from "../../../../components/ui/button"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "../../../../components/ui/dropdown-menu"; -import useFlowStore from "../../../../stores/flowStore"; import { outputComponentType } from "../../../../types/components"; -import { NodeDataType } from "../../../../types/flow"; import { cn } from "../../../../utils/utils"; export default function OutputComponent({ @@ -23,9 +11,6 @@ export default function OutputComponent({ name, proxy, }: outputComponentType) { - const setNode = useFlowStore((state) => state.setNode); - const updateNodeInternals = useUpdateNodeInternals(); - const displayProxy = (children) => { if (proxy) { return ( diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index e0a582d445c4..09dd6f6a4e23 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -289,7 +289,6 @@ export default function GenericNode({ function handlePlayWShortcut() { if (buildStatus === BuildStatus.BUILDING || isBuilding || !selected) return; setValidationStatus(null); - console.log(data.node?.display_name); buildFlow({ stopNodeId: data.id }); } diff --git a/src/frontend/src/components/authSettingsGuard/index.tsx b/src/frontend/src/components/authSettingsGuard/index.tsx new file mode 100644 index 000000000000..90bf63dd93c0 --- /dev/null +++ b/src/frontend/src/components/authSettingsGuard/index.tsx @@ -0,0 +1,19 @@ +import FeatureFlags from "@/../feature-config.json"; +import useAuthStore from "@/stores/authStore"; +import { useStoreStore } from "@/stores/storeStore"; +import { Navigate } from "react-router-dom"; + +export const AuthSettingsGuard = ({ children }) => { + const autoLogin = useAuthStore((state) => state.autoLogin); + const hasStore = useStoreStore((state) => state.hasStore); + + // Hides the General settings if there is nothing to show + const showGeneralSettings = + FeatureFlags.ENABLE_PROFILE_ICONS || hasStore || !autoLogin; + + if (showGeneralSettings) { + return children; + } else { + return ; + } +}; diff --git a/src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx b/src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx deleted file mode 100644 index e9236e98be14..000000000000 --- a/src/frontend/src/components/cardComponent/hooks/use-playground-effect.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { useEffect } from "react"; -import { FlowType } from "../../../types/flow"; - -const usePlaygroundEffect = ( - currentFlowId: string, - playground: boolean, - openPlayground: boolean, - currentFlow: FlowType | undefined, - setNodes: (value: any, value2: boolean) => void, - setEdges: (value: any, value2: boolean) => void, - cleanFlowPool: () => void, -) => { - useEffect(() => { - if (currentFlowId && playground) { - if (openPlayground) { - setNodes(currentFlow?.data?.nodes ?? [], true); - setEdges(currentFlow?.data?.edges ?? [], true); - } else { - setNodes([], true); - setEdges([], true); - } - cleanFlowPool(); - } - }, [openPlayground]); -}; - -export default usePlaygroundEffect; diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index 0e6af1f35f27..c36aef2913d8 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -1,12 +1,10 @@ import { usePostLikeComponent } from "@/controllers/API/queries/store"; -import { useEffect, useState } from "react"; -import { createRoot } from "react-dom/client"; +import { useState } from "react"; import { Control } from "react-hook-form"; -import { getComponent, postLikeComponent } from "../../controllers/API"; +import { getComponent } from "../../controllers/API"; import IOModal from "../../modals/IOModal"; import DeleteConfirmationModal from "../../modals/deleteConfirmationModal"; import useAlertStore from "../../stores/alertStore"; -import useFlowStore from "../../stores/flowStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; import { useStoreStore } from "../../stores/storeStore"; import { FlowType } from "../../types/flow"; @@ -31,7 +29,6 @@ import Loading from "../ui/loading"; import useDataEffect from "./hooks/use-data-effect"; import useInstallComponent from "./hooks/use-handle-install"; import useDragStart from "./hooks/use-on-drag-start"; -import usePlaygroundEffect from "./hooks/use-playground-effect"; import { convertTestName } from "./utils/convert-test-name"; export default function CollectionCardComponent({ @@ -56,7 +53,6 @@ export default function CollectionCardComponent({ const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const setValidApiKey = useStoreStore((state) => state.updateValidApiKey); - const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool); const isStore = false; const [loading, setLoading] = useState(false); const [likedByUser, setLikedByUser] = useState(data?.liked_by_user ?? false); @@ -64,16 +60,9 @@ export default function CollectionCardComponent({ const [downloadsCount, setDownloadsCount] = useState( data?.downloads_count ?? 0, ); - const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); const getFlowById = useFlowsManagerStore((state) => state.getFlowById); - const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); - const setNodes = useFlowStore((state) => state.setNodes); - const setEdges = useFlowStore((state) => state.setEdges); const [openPlayground, setOpenPlayground] = useState(false); - const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId, - ); const [loadingPlayground, setLoadingPlayground] = useState(false); const selectedFlowsComponentsCards = useFlowsManagerStore( @@ -96,16 +85,6 @@ export default function CollectionCardComponent({ return inputs.length > 0 || outputs.length > 0; } - usePlaygroundEffect( - currentFlowId, - playground!, - openPlayground, - currentFlow, - setNodes, - setEdges, - cleanFlowPool, - ); - useDataEffect(data, setLikedByUser, setLikesCount, setDownloadsCount); const { handleInstall } = useInstallComponent( @@ -317,7 +296,7 @@ export default function CollectionCardComponent({ setLoadingPlayground(false); return; } - setCurrentFlowId(data.id); + setCurrentFlow(flow); setOpenPlayground(true); setLoadingPlayground(false); } else { @@ -473,7 +452,7 @@ export default function CollectionCardComponent({ setLoadingPlayground(false); return; } - setCurrentFlowId(data.id); + setCurrentFlow(flow); setOpenPlayground(true); setLoadingPlayground(false); } else { @@ -510,6 +489,7 @@ export default function CollectionCardComponent({ {openPlayground && ( state.hasStore); const validApiKey = useStoreStore((state) => state.validApiKey); const hasApiKey = useStoreStore((state) => state.hasApiKey); - const currentFlow = useFlowsManagerStore((state) => state.currentFlow); - - const prevNodesRef = useRef(); + const currentFlow = useFlowStore((state) => state.currentFlow); const ModalMemo = useMemo( () => ( diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index 239a3117e0c4..f17afa61571b 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -86,6 +86,7 @@ export default function CodeTabsComponent({ }} id="tweaks-switch" onCheckedChange={setActiveTweaks} + checked={activeTweaks} autoFocus={false} />

Docs - - Free Cloud Service - - Join our Discord - - Follow us on X + Free Cloud Service +