From fb0525e0ce49d2758f3a5494ce523081f3d068c6 Mon Sep 17 00:00:00 2001 From: Kevin Lin Date: Thu, 24 Jul 2025 10:14:28 -0700 Subject: [PATCH 01/24] fix: update test sources with file limits (#3521) --- letta/services/agent_manager.py | 8 ++++++++ tests/test_sources.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index 6302f645..be12684f 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -15,6 +15,8 @@ from letta.constants import ( BASE_TOOLS, BASE_VOICE_SLEEPTIME_CHAT_TOOLS, BASE_VOICE_SLEEPTIME_TOOLS, + DEFAULT_CORE_MEMORY_SOURCE_CHAR_LIMIT, + DEFAULT_MAX_FILES_OPEN, DEFAULT_TIMEZONE, DEPRECATED_LETTA_TOOLS, FILES_TOOLS, @@ -3169,6 +3171,12 @@ class AgentManager: if max_files is None: max_files = default_max_files + # FINAL fallback: ensure neither is None (should never happen, but just in case) + if per_file_limit is None: + per_file_limit = DEFAULT_CORE_MEMORY_SOURCE_CHAR_LIMIT + if max_files is None: + max_files = DEFAULT_MAX_FILES_OPEN + return per_file_limit, max_files @enforce_types diff --git a/tests/test_sources.py b/tests/test_sources.py index bfbf6a14..cb956f6c 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -277,6 +277,10 @@ def test_attach_existing_files_creates_source_blocks_correctly(disable_pinecone, # Assert that the expected chunk is in the raw system message expected_chunk = """ + +- current_files_open=1 +- max_files_open=5 + @@ -334,6 +338,10 @@ def test_delete_source_removes_source_blocks_correctly(disable_pinecone, client: raw_system_message = get_raw_system_message(client, agent_state.id) # Assert that the expected chunk is in the raw system message expected_chunk = """ + +- current_files_open=1 +- max_files_open=5 + From 2b0cc0e9a11434f6bed3989e3e5e74cbac785cf4 Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 24 Jul 2025 10:52:38 -0700 Subject: [PATCH 02/24] fix: Fix open/close files tests and upgrade letta client (#3535) --- poetry.lock | 592 +++++------------------------------------- pyproject.toml | 2 +- tests/test_sources.py | 10 +- 3 files changed, 76 insertions(+), 528 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4c6a02aa..b4921d5b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aioboto3" @@ -6,8 +6,6 @@ version = "14.3.0" description = "Async boto3 wrapper" optional = true python-versions = "<4.0,>=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"}, {file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"}, @@ -27,8 +25,6 @@ version = "2.22.0" description = "Async client for aws services using botocore and aiohttp" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"}, {file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"}, @@ -54,8 +50,6 @@ version = "24.1.0" description = "File support for asyncio." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, @@ -67,7 +61,6 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -79,7 +72,6 @@ version = "3.12.13" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, @@ -180,7 +172,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] [[package]] name = "aiohttp-retry" @@ -188,8 +180,6 @@ version = "2.9.1" description = "Simple retry client for aiohttp" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"}, {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"}, @@ -204,8 +194,6 @@ version = "0.12.0" description = "itertools and builtins for AsyncIO and mixed iterables" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, @@ -221,14 +209,13 @@ version = "0.9.1" description = "AsyncIO version of the standard multiprocessing module" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "aiomultiprocess-0.9.1-py3-none-any.whl", hash = "sha256:3a7b3bb3c38dbfb4d9d1194ece5934b6d32cf0280e8edbe64a7d215bba1322c6"}, {file = "aiomultiprocess-0.9.1.tar.gz", hash = "sha256:f0231dbe0291e15325d7896ebeae0002d95a4f2675426ca05eb35f24c60e495b"}, ] [package.extras] -dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0) ; sys_platform != \"win32\""] +dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0)"] docs = ["sphinx (==7.3.7)", "sphinx-mdinclude (==0.6.0)"] [[package]] @@ -237,7 +224,6 @@ version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, @@ -253,7 +239,6 @@ version = "0.21.0" description = "asyncio bridge to the standard sqlite3 module" optional = false python-versions = ">=3.9" -groups = ["main", "sqlite"] files = [ {file = "aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0"}, {file = "aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3"}, @@ -272,7 +257,6 @@ version = "1.16.3" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "alembic-1.16.3-py3-none-any.whl", hash = "sha256:70a7c7829b792de52d08ca0e3aefaf060687cb8ed6bebfa557e597a1a5e5a481"}, {file = "alembic-1.16.3.tar.gz", hash = "sha256:18ad13c1f40a5796deee4b2346d1a9c382f44b8af98053897484fa6cf88025e4"}, @@ -293,7 +277,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -305,7 +288,6 @@ version = "0.49.0" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "anthropic-0.49.0-py3-none-any.whl", hash = "sha256:bbc17ad4e7094988d2fa86b87753ded8dce12498f4b85fe5810f208f454a8375"}, {file = "anthropic-0.49.0.tar.gz", hash = "sha256:c09e885b0f674b9119b4f296d8508907f6cff0009bc20d5cf6b35936c40b4398"}, @@ -330,7 +312,6 @@ version = "4.9.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, @@ -344,7 +325,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] +test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -353,8 +334,6 @@ version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = ">=3.6" -groups = ["dev"] -markers = "platform_system == \"Darwin\"" files = [ {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, @@ -366,7 +345,6 @@ version = "3.11.0" description = "In-process task scheduler with Cron-like capabilities" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"}, {file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"}, @@ -383,7 +361,7 @@ mongodb = ["pymongo (>=3.0)"] redis = ["redis (>=3.0)"] rethinkdb = ["rethinkdb (>=2.4.0)"] sqlalchemy = ["sqlalchemy (>=1.4)"] -test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6 ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "anyio (>=4.5.2)", "gevent ; python_version < \"3.14\"", "pytest", "pytz", "twisted ; python_version < \"3.14\""] +test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6", "anyio (>=4.5.2)", "gevent", "pytest", "pytz", "twisted"] tornado = ["tornado (>=4.3)"] twisted = ["twisted"] zookeeper = ["kazoo"] @@ -394,7 +372,6 @@ version = "3.6.2" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, @@ -409,8 +386,6 @@ version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, @@ -422,12 +397,10 @@ version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] astroid = ["astroid (>=2,<4)"] @@ -437,10 +410,8 @@ test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -optional = true +optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "(extra == \"redis\" or extra == \"all\") and python_full_version < \"3.11.3\" or python_version == \"3.10\"" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, @@ -452,8 +423,6 @@ version = "0.30.0" description = "An asyncio PostgreSQL driver" optional = true python-versions = ">=3.8.0" -groups = ["main"] -markers = "extra == \"postgres\"" files = [ {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, @@ -511,8 +480,8 @@ async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.11.0\""} [package.extras] docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] -gssauth = ["gssapi ; platform_system != \"Windows\"", "sspilib ; platform_system == \"Windows\""] -test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi ; platform_system == \"Linux\"", "k5test ; platform_system == \"Linux\"", "mypy (>=1.8.0,<1.9.0)", "sspilib ; platform_system == \"Windows\"", "uvloop (>=0.15.3) ; platform_system != \"Windows\" and python_version < \"3.14.0\""] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] [[package]] name = "attrs" @@ -520,19 +489,18 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] +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 = "autoflake" @@ -540,8 +508,6 @@ version = "2.3.1" description = "Removes unused imports and unused variables" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840"}, {file = "autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e"}, @@ -557,7 +523,6 @@ version = "2.1.3" description = "A prompt programming language" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "banks-2.1.3-py3-none-any.whl", hash = "sha256:9e1217dc977e6dd1ce42c5ff48e9bcaf238d788c81b42deb6a555615ffcffbab"}, {file = "banks-2.1.3.tar.gz", hash = "sha256:c0dd2cb0c5487274a513a552827e6a8ddbd0ab1a1b967f177e71a6e4748a3ed2"}, @@ -579,7 +544,6 @@ version = "4.3.0" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281"}, {file = "bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb"}, @@ -644,7 +608,6 @@ version = "4.13.4" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" -groups = ["main"] files = [ {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, @@ -667,8 +630,6 @@ version = "0.23.1" description = "The bidirectional mapping library for Python." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"}, {file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"}, @@ -680,7 +641,6 @@ version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, @@ -729,8 +689,6 @@ version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, @@ -742,8 +700,6 @@ version = "1.37.3" description = "The AWS SDK for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "boto3-1.37.3-py3-none-any.whl", hash = "sha256:2063b40af99fd02f6228ff52397b552ff3353831edaf8d25cc04801827ab9794"}, {file = "boto3-1.37.3.tar.gz", hash = "sha256:21f3ce0ef111297e63a6eb998a25197b8c10982970c320d4c6e8db08be2157be"}, @@ -763,8 +719,6 @@ version = "1.37.3" description = "Low-level, data-driven core of boto 3." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "botocore-1.37.3-py3-none-any.whl", hash = "sha256:d01bd3bf4c80e61fa88d636ad9f5c9f60a551d71549b481386c6b4efe0bb2b2e"}, {file = "botocore-1.37.3.tar.gz", hash = "sha256:fe8403eb55a88faf9b0f9da6615e5bee7be056d75e17af66c3c8f0a3b0648da4"}, @@ -784,7 +738,6 @@ version = "1.1.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, @@ -796,10 +749,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -812,14 +761,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -830,24 +773,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -857,10 +784,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -872,10 +795,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -888,10 +807,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -904,10 +819,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -919,8 +830,6 @@ version = "5.5.2" description = "Extensible memoizing collections and decorators" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -932,7 +841,6 @@ version = "2025.7.9" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39"}, {file = "certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079"}, @@ -944,7 +852,6 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -1014,7 +921,6 @@ files = [ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] -markers = {dev = "implementation_name == \"pypy\""} [package.dependencies] pycparser = "*" @@ -1025,8 +931,6 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -1038,7 +942,6 @@ version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -1140,7 +1043,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -1155,7 +1057,6 @@ version = "0.1.4" description = "Create data objects" optional = false python-versions = ">=3.5" -groups = ["main"] files = [ {file = "cobble-0.1.4-py3-none-any.whl", hash = "sha256:36c91b1655e599fd428e2b95fdd5f0da1ca2e9f1abb0bc871dec21a0e78a2b44"}, {file = "cobble-0.1.4.tar.gz", hash = "sha256:de38be1539992c8a06e569630717c485a5f91be2192c461ea2b220607dfa78aa"}, @@ -1167,7 +1068,6 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev", "dev,tests"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -1179,7 +1079,6 @@ 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.*" -groups = ["main"] files = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, @@ -1197,7 +1096,6 @@ version = "0.2.2" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, @@ -1215,7 +1113,6 @@ version = "0.7.20" description = "Core package to act as a bridge between composio platform and other services." optional = false python-versions = "<4,>=3.9" -groups = ["main"] files = [ {file = "composio_core-0.7.20-py3-none-any.whl", hash = "sha256:e1cfb9cfc68a4622bc15827143ddf726f429d281e8f9de5d4c0965e75d039f14"}, {file = "composio_core-0.7.20.tar.gz", hash = "sha256:1dc29dbf73eb72d2df1c5b0d4d2f21459d15029322cf74df8fdecc44dcaeb1f4"}, @@ -1254,8 +1151,6 @@ version = "1.7.1" description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." optional = true python-versions = ">=3.6" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6"}, {file = "configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9"}, @@ -1271,7 +1166,6 @@ version = "1.3.2" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934"}, {file = "contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989"}, @@ -1348,7 +1242,6 @@ version = "45.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" -groups = ["main"] files = [ {file = "cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8"}, {file = "cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d"}, @@ -1393,10 +1286,10 @@ files = [ cffi = {version = ">=1.14", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs ; python_full_version >= \"3.8.0\"", "sphinx-rtd-theme (>=3.0.0) ; python_full_version >= \"3.8.0\""] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_full_version >= \"3.8.0\""] -pep8test = ["check-sdist ; python_full_version >= \"3.8.0\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==45.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -1408,7 +1301,6 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -1424,7 +1316,6 @@ version = "0.6.7" description = "Easily serialize dataclasses to and from JSON." optional = false python-versions = "<4.0,>=3.7" -groups = ["main"] files = [ {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, @@ -1440,7 +1331,6 @@ version = "0.25.9" description = "Datamodel Code Generator" optional = false python-versions = "<4.0,>=3.7" -groups = ["main"] files = [ {file = "datamodel_code_generator-0.25.9-py3-none-any.whl", hash = "sha256:9e0324233123d6e39a35bc0004771956935889a974aacfd7a0651de11d2219a9"}, {file = "datamodel_code_generator-0.25.9.tar.gz", hash = "sha256:65ca9807d8edbd88a7f7931c10f4bc1c08bd9bbc5bb0508418a2b6a16590eb65"}, @@ -1456,9 +1346,9 @@ isort = ">=4.3.21,<6.0" jinja2 = ">=2.10.1,<4.0" packaging = "*" pydantic = [ + {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.10.0,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, - {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"4.0\""}, - {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version == \"3.10\""}, ] pyyaml = ">=6.0.1" toml = {version = ">=0.10.0,<1.0.0", markers = "python_version < \"3.11\""} @@ -1475,7 +1365,6 @@ version = "1.8.14" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "debugpy-1.8.14-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:93fee753097e85623cab1c0e6a68c76308cd9f13ffdf44127e6fab4fbf024339"}, {file = "debugpy-1.8.14-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d937d93ae4fa51cdc94d3e865f535f185d5f9748efb41d0d49e33bf3365bd79"}, @@ -1511,12 +1400,10 @@ version = "5.2.1" description = "Decorators for Humans" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [[package]] name = "defusedxml" @@ -1524,7 +1411,6 @@ version = "0.7.1" description = "XML bomb protection for Python stdlib modules" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["main"] files = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, @@ -1536,7 +1422,6 @@ version = "3.0.6" description = "encoder, decoder, and lint/validator for JSON (JavaScript Object Notation) compliant with RFC 7159" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "demjson3-3.0.6.tar.gz", hash = "sha256:37c83b0c6eb08d25defc88df0a2a4875d58a7809a9650bd6eee7afd8053cdbac"}, ] @@ -1547,7 +1432,6 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["main"] files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -1557,7 +1441,7 @@ files = [ wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] [[package]] name = "dirtyjson" @@ -1565,7 +1449,6 @@ version = "1.0.8" description = "JSON decoder for Python that can extract data from the muck" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53"}, {file = "dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd"}, @@ -1577,8 +1460,6 @@ version = "0.3.9" description = "Distribution utilities" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -1590,7 +1471,6 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -1602,7 +1482,6 @@ version = "2.7.0" description = "DNS toolkit" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -1623,8 +1502,6 @@ version = "7.1.0" description = "A Python library for the Docker Engine API." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, @@ -1647,7 +1524,6 @@ version = "0.16" description = "Parse Python docstrings in reST, Google and Numpydoc format" optional = false python-versions = ">=3.6,<4.0" -groups = ["main"] files = [ {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, @@ -1659,8 +1535,6 @@ version = "1.5.5" description = "E2B SDK that give agents cloud environments" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"cloud-tool-sandbox\"" files = [ {file = "e2b-1.5.5-py3-none-any.whl", hash = "sha256:cd6343193f5b941af33504d422cb39c02515a23a5c94c9ef9fdebeb140fb382e"}, {file = "e2b-1.5.5.tar.gz", hash = "sha256:541dd0bd8b3ff8aa1f56a2719333d5b8e0465c30df7ebd93e2776cc15672503a"}, @@ -1681,8 +1555,6 @@ version = "1.5.2" description = "E2B Code Interpreter - Stateful code execution" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"cloud-tool-sandbox\"" files = [ {file = "e2b_code_interpreter-1.5.2-py3-none-any.whl", hash = "sha256:5c3188d8f25226b28fef4b255447cc6a4c36afb748bdd5180b45be486d5169f3"}, {file = "e2b_code_interpreter-1.5.2.tar.gz", hash = "sha256:3bd6ea70596290e85aaf0a2f19f28bf37a5e73d13086f5e6a0080bb591c5a547"}, @@ -1699,7 +1571,6 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -1715,7 +1586,6 @@ version = "0.2.2" description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, @@ -1730,8 +1600,6 @@ version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["main", "dev", "dev,tests"] -markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -1749,15 +1617,13 @@ version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] [[package]] name = "faker" @@ -1765,7 +1631,6 @@ version = "36.2.3" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "Faker-36.2.3-py3-none-any.whl", hash = "sha256:7ca995d65ec08d013f3c1230da7f628cb2c169a77e89cd265d7a59f443f0febd"}, {file = "faker-36.2.3.tar.gz", hash = "sha256:1ed2d7a9c3a5657fc11a4298e8cf19f71d83740560d4ed0895b30399d482d538"}, @@ -1780,7 +1645,6 @@ version = "0.115.14" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, @@ -1801,8 +1665,6 @@ version = "3.18.0" description = "A platform independent file lock." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, @@ -1811,7 +1673,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "filetype" @@ -1819,7 +1681,6 @@ version = "1.2.0" description = "Infer file type and MIME type of any file/buffer. No external dependencies." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, @@ -1831,8 +1692,6 @@ version = "2.15.0" description = "Python SDK for Firecrawl API" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"external-tools\"" files = [ {file = "firecrawl_py-2.15.0-py3-none-any.whl", hash = "sha256:6e4c53f029fe4784854549cf2760a7ea6a7faa2a098ce9fd49dd85e2f7004186"}, {file = "firecrawl_py-2.15.0.tar.gz", hash = "sha256:8bc8eea586f1fc81bac8692faa34f362050ff8045298b71d04140239c843349b"}, @@ -1852,8 +1711,6 @@ version = "3.1.1" description = "A simple framework for building complex web applications." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, @@ -1877,8 +1734,6 @@ version = "6.0.1" description = "A Flask extension simplifying CORS support" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "flask_cors-6.0.1-py3-none-any.whl", hash = "sha256:c7b2cbfb1a31aa0d2e5341eea03a6805349f7a61647daee1a15c46bbe981494c"}, {file = "flask_cors-6.0.1.tar.gz", hash = "sha256:d81bcb31f07b0985be7f48406247e9243aced229b7747219160a0559edd678db"}, @@ -1894,8 +1749,6 @@ version = "0.6.3" description = "User authentication and session management for Flask." optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333"}, {file = "Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d"}, @@ -1911,7 +1764,6 @@ version = "25.2.10" description = "The FlatBuffers serialization format for Python" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051"}, {file = "flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e"}, @@ -1923,7 +1775,6 @@ version = "4.58.5" description = "Tools to manipulate font files" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d500d399aa4e92d969a0d21052696fa762385bb23c3e733703af4a195ad9f34c"}, {file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b00530b84f87792891874938bd42f47af2f7f4c2a1d70466e6eb7166577853ab"}, @@ -1970,18 +1821,18 @@ files = [ ] [package.extras] -all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""] +interpolatable = ["munkres", "pycairo", "scipy"] lxml = ["lxml (>=4.0)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] repacker = ["uharfbuzz (>=0.23.0)"] symfont = ["sympy"] -type1 = ["xattr ; sys_platform == \"darwin\""] +type1 = ["xattr"] ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] -woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "frozenlist" @@ -1989,7 +1840,6 @@ version = "1.7.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, @@ -2103,7 +1953,6 @@ version = "2025.5.1" description = "File-system specification" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462"}, {file = "fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475"}, @@ -2143,7 +1992,6 @@ version = "1.3.0" description = "GenSON is a powerful, user-friendly JSON Schema generator." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7"}, {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, @@ -2155,8 +2003,6 @@ version = "25.5.1" description = "Coroutine-based network library" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "gevent-25.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8e5a0fab5e245b15ec1005b3666b0a2e867c26f411c8fe66ae1afe07174a30e9"}, {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7b80a37f2fb45ee4a8f7e64b77dd8a842d364384046e394227b974a4e9c9a52"}, @@ -2206,11 +2052,11 @@ greenlet = {version = ">=3.2.2", markers = "platform_python_implementation == \" "zope.interface" = "*" [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""] +dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] -recommended = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] -test = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"] +monitor = ["psutil (>=5.7.0)"] +recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] [[package]] name = "geventhttpclient" @@ -2218,8 +2064,6 @@ version = "2.3.4" description = "HTTP client library for gevent" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "geventhttpclient-2.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:182f5158504ac426d591cfb1234de5180813292b49049e761f00bf70691aace5"}, {file = "geventhttpclient-2.3.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59a2e7c136a3e6b60b87bf8b87e5f1fb25705d76ab7471018e25f8394c640dda"}, @@ -2324,8 +2168,6 @@ version = "2.40.3" description = "Google Authentication Library" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca"}, {file = "google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77"}, @@ -2339,11 +2181,11 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"] enterprise-cert = ["cryptography", "pyopenssl"] -pyjwt = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"] -pyopenssl = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +pyjwt = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"] +pyopenssl = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0)"] -testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"] +testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0)", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"] urllib3 = ["packaging", "urllib3"] [[package]] @@ -2352,8 +2194,6 @@ version = "1.25.0" description = "GenAI Python SDK" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "google_genai-1.25.0-py3-none-any.whl", hash = "sha256:fb5cee79b9a0a1b2afd5cfdf279099ecebd186551eefcaa6ec0c6016244e6138"}, {file = "google_genai-1.25.0.tar.gz", hash = "sha256:a08a79c819a5d949d9948cd372e36e512bf85cd28158994daaa36d0ec4cb2b02"}, @@ -2378,7 +2218,6 @@ version = "1.70.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, @@ -2396,8 +2235,6 @@ version = "2.4.1" description = "A Rust HTTP server for Python applications" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "granian-2.4.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7a5279a4d6664f1aa60826af6e3588d890732067c8f6266946d9810452e616ea"}, {file = "granian-2.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:42c93f33914d9de8f79ce4bfe50f8b640733865831c4ec020199c9c57bf52cfd"}, @@ -2512,8 +2349,8 @@ all = ["granian[dotenv,pname,reload]"] dotenv = ["python-dotenv (>=1.1,<2.0)"] pname = ["setproctitle (>=1.3.3,<1.4.0)"] reload = ["watchfiles (>=1.0,<2.0)"] -rloop = ["rloop (>=0.1,<1.0) ; sys_platform != \"win32\""] -uvloop = ["uvloop (>=0.18.0) ; platform_python_implementation == \"CPython\" and sys_platform != \"win32\""] +rloop = ["rloop (>=0.1,<1.0)"] +uvloop = ["uvloop (>=0.18.0)"] [[package]] name = "greenlet" @@ -2521,8 +2358,6 @@ version = "3.2.3" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or (extra == \"dev\" or extra == \"desktop\" or extra == \"all\") and platform_python_implementation == \"CPython\"" files = [ {file = "greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be"}, {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac"}, @@ -2590,7 +2425,6 @@ version = "1.7.3" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75"}, {file = "griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b"}, @@ -2605,7 +2439,6 @@ version = "1.73.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "grpcio-1.73.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:2d70f4ddd0a823436c2624640570ed6097e40935c9194482475fe8e3d9754d55"}, {file = "grpcio-1.73.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:3841a8a5a66830261ab6a3c2a3dc539ed84e4ab019165f77b3eeb9f0ba621f26"}, @@ -2669,7 +2502,6 @@ version = "1.71.2" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "grpcio_tools-1.71.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:ab8a28c2e795520d6dc6ffd7efaef4565026dbf9b4f5270de2f3dd1ce61d2318"}, {file = "grpcio_tools-1.71.2-cp310-cp310-macosx_10_14_universal2.whl", hash = "sha256:654ecb284a592d39a85556098b8c5125163435472a20ead79b805cf91814b99e"}, @@ -2735,7 +2567,6 @@ version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, @@ -2747,7 +2578,6 @@ version = "2020.1.16" description = "Turn HTML into equivalent Markdown-structured text." optional = false python-versions = ">=3.5" -groups = ["main"] files = [ {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, @@ -2759,7 +2589,6 @@ version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, @@ -2781,7 +2610,6 @@ version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -2794,7 +2622,7 @@ httpcore = "==1.*" idna = "*" [package.extras] -brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -2806,7 +2634,6 @@ version = "0.4.0" description = "Consume Server-Sent Event (SSE) messages with HTTPX." optional = false python-versions = ">=3.8" -groups = ["main"] 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"}, @@ -2818,7 +2645,6 @@ 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.*" -groups = ["main"] files = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, @@ -2833,8 +2659,6 @@ version = "2.6.12" description = "File identification library for Python" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, @@ -2849,7 +2673,6 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -2864,7 +2687,6 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -2874,12 +2696,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -2888,7 +2710,6 @@ version = "5.6.2" description = "Correctly generate plurals, singular nouns, ordinals, indefinite articles; convert numbers to words" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238"}, {file = "inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9"}, @@ -2896,7 +2717,7 @@ files = [ [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] +testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "inflection" @@ -2904,7 +2725,6 @@ version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" optional = false python-versions = ">=3.5" -groups = ["main"] files = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -2916,12 +2736,10 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [[package]] name = "ipdb" @@ -2929,15 +2747,14 @@ version = "0.13.13" description = "IPython-enabled pdb" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] files = [ {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, ] [package.dependencies] -decorator = {version = "*", markers = "python_version >= \"3.11\""} -ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} +decorator = {version = "*", markers = "python_version > \"3.6\""} +ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""} tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} [[package]] @@ -2946,7 +2763,6 @@ version = "6.29.5" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, @@ -2980,12 +2796,10 @@ version = "8.37.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" -groups = ["main", "dev"] files = [ {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"}, {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} @@ -3003,7 +2817,7 @@ typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -3020,7 +2834,6 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" -groups = ["main"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -3035,8 +2848,6 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -3048,12 +2859,10 @@ version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] parso = ">=0.8.4,<0.9.0" @@ -3069,7 +2878,6 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -3087,7 +2895,6 @@ version = "0.10.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303"}, {file = "jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e"}, @@ -3174,8 +2981,6 @@ version = "1.0.1" description = "JSON Matching Expressions" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, @@ -3187,7 +2992,6 @@ version = "1.5.1" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a"}, {file = "joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444"}, @@ -3199,8 +3003,6 @@ version = "1.33" description = "Apply JSON-Patches (RFC 6902)" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, @@ -3215,8 +3017,6 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -3228,7 +3028,6 @@ version = "1.1.0" description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, @@ -3240,7 +3039,6 @@ version = "4.24.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d"}, {file = "jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196"}, @@ -3262,7 +3060,6 @@ version = "2025.4.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, @@ -3277,7 +3074,6 @@ version = "8.6.3" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, @@ -3292,7 +3088,7 @@ traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-core" @@ -3300,7 +3096,6 @@ version = "5.8.1" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0"}, {file = "jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941"}, @@ -3321,7 +3116,6 @@ version = "1.4.8" description = "A fast implementation of the Cassowary constraint solver" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, @@ -3411,8 +3205,6 @@ version = "0.3.26" description = "Building applications with LLMs through composability" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain-0.3.26-py3-none-any.whl", hash = "sha256:361bb2e61371024a8c473da9f9c55f4ee50f269c5ab43afdb2b1309cb7ac36cf"}, {file = "langchain-0.3.26.tar.gz", hash = "sha256:8ff034ee0556d3e45eff1f1e96d0d745ced57858414dba7171c8ebdbeb5580c9"}, @@ -3453,8 +3245,6 @@ version = "0.3.27" description = "Community contributed LangChain integrations." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_community-0.3.27-py3-none-any.whl", hash = "sha256:581f97b795f9633da738ea95da9cb78f8879b538090c9b7a68c0aed49c828f0d"}, {file = "langchain_community-0.3.27.tar.gz", hash = "sha256:e1037c3b9da0c6d10bf06e838b034eb741e016515c79ef8f3f16e53ead33d882"}, @@ -3483,8 +3273,6 @@ version = "0.3.68" description = "Building applications with LLMs through composability" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_core-0.3.68-py3-none-any.whl", hash = "sha256:5e5c1fbef419590537c91b8c2d86af896fbcbaf0d5ed7fdcdd77f7d8f3467ba0"}, {file = "langchain_core-0.3.68.tar.gz", hash = "sha256:312e1932ac9aa2eaf111b70fdc171776fa571d1a86c1f873dcac88a094b19c6f"}, @@ -3505,8 +3293,6 @@ version = "0.3.8" description = "LangChain text splitting utilities" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02"}, {file = "langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e"}, @@ -3521,8 +3307,6 @@ version = "0.4.4" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langsmith-0.4.4-py3-none-any.whl", hash = "sha256:014c68329bd085bd6c770a6405c61bb6881f82eb554ce8c4d1984b0035fd1716"}, {file = "langsmith-0.4.4.tar.gz", hash = "sha256:70c53bbff24a7872e88e6fa0af98270f4986a6e364f9e85db1cc5636defa4d66"}, @@ -3545,14 +3329,13 @@ pytest = ["pytest (>=7.0.0)", "rich (>=13.9.4,<14.0.0)"] [[package]] name = "letta-client" -version = "0.1.219" +version = "0.1.220" description = "" optional = false python-versions = "<4.0,>=3.8" -groups = ["main"] files = [ - {file = "letta_client-0.1.219-py3-none-any.whl", hash = "sha256:f5ad81163f60f5750fce1422e66df318be34f8731d8eeb427750c6dc7d7e698d"}, - {file = "letta_client-0.1.219.tar.gz", hash = "sha256:eb75ac70222770dc185f3b7ed9982c168aabc3993836f8c92e1b835317714781"}, + {file = "letta_client-0.1.220-py3-none-any.whl", hash = "sha256:ba5faaadc2f8819d846ca2381dc3d757525f6af97822fba31f0643edd521eeef"}, + {file = "letta_client-0.1.220.tar.gz", hash = "sha256:cebc7e31cae7887d7aa684803a6c4679fa9e1ecdc1e1572f1b23934969f69523"}, ] [package.dependencies] @@ -3568,7 +3351,6 @@ version = "0.1.32" description = "" optional = false python-versions = "<4,>=3.8" -groups = ["main"] files = [ {file = "llama_cloud-0.1.32-py3-none-any.whl", hash = "sha256:c42b2d5fb24acc8595bcc3626fb84c872909a16ab6d6879a1cb1101b21c238bd"}, {file = "llama_cloud-0.1.32.tar.gz", hash = "sha256:cea98241127311ea91f191c3c006aa6558f01d16f9539ed93b24d716b888f10e"}, @@ -3585,7 +3367,6 @@ version = "0.6.43" description = "Tailored SDK clients for LlamaCloud services." optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_cloud_services-0.6.43-py3-none-any.whl", hash = "sha256:2349195f501ba9151ea3ab384d20cae8b4dc4f335f60bd17607332626bdfa2e4"}, {file = "llama_cloud_services-0.6.43.tar.gz", hash = "sha256:fa6be33bf54d467cace809efee8c2aeeb9de74ce66708513d37b40d738d3350f"}, @@ -3606,7 +3387,6 @@ version = "0.12.42" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index-0.12.42-py3-none-any.whl", hash = "sha256:9a304b2bd71d6772fc6c2e9995e119815c00716fc98813b15ea79caee09f1fe2"}, {file = "llama_index-0.12.42.tar.gz", hash = "sha256:d2bd2b9ea06bc42ebe74697aaa0b66c79bd3dee8ff5d06c53294d49ca44512e7"}, @@ -3632,7 +3412,6 @@ version = "0.4.12" description = "llama-index agent openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_agent_openai-0.4.12-py3-none-any.whl", hash = "sha256:6dbb6276b2e5330032a726b28d5eef5140825f36d72d472b231f08ad3af99665"}, {file = "llama_index_agent_openai-0.4.12.tar.gz", hash = "sha256:d2fe53feb69cfe45752edb7328bf0d25f6a9071b3c056787e661b93e5b748a28"}, @@ -3649,7 +3428,6 @@ version = "0.4.4" description = "llama-index cli" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_cli-0.4.4-py3-none-any.whl", hash = "sha256:1070593cf79407054735ab7a23c5a65a26fc18d264661e42ef38fc549b4b7658"}, {file = "llama_index_cli-0.4.4.tar.gz", hash = "sha256:c3af0cf1e2a7e5ef44d0bae5aa8e8872b54c5dd6b731afbae9f13ffeb4997be0"}, @@ -3666,7 +3444,6 @@ version = "0.12.42" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_core-0.12.42-py3-none-any.whl", hash = "sha256:0534cd9a4f6113175aa406a47ae9a683b5a43fd55532e9dbbffa96838ff18e07"}, {file = "llama_index_core-0.12.42.tar.gz", hash = "sha256:cff21fe15610826997c876a6b1d28d52727932c5f9c2af04b23e041a10f40a24"}, @@ -3704,7 +3481,6 @@ version = "0.3.1" description = "llama-index embeddings openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_embeddings_openai-0.3.1-py3-none-any.whl", hash = "sha256:f15a3d13da9b6b21b8bd51d337197879a453d1605e625a1c6d45e741756c0290"}, {file = "llama_index_embeddings_openai-0.3.1.tar.gz", hash = "sha256:1368aad3ce24cbaed23d5ad251343cef1eb7b4a06d6563d6606d59cb347fef20"}, @@ -3720,7 +3496,6 @@ version = "0.7.10" description = "llama-index indices llama-cloud integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_indices_managed_llama_cloud-0.7.10-py3-none-any.whl", hash = "sha256:f7edcfb8f694cab547cd9324be7835dc97470ce05150d0b8888fa3bf9d2f84a8"}, {file = "llama_index_indices_managed_llama_cloud-0.7.10.tar.gz", hash = "sha256:53267907e23d8fbcbb97c7a96177a41446de18550ca6030276092e73b45ca880"}, @@ -3736,7 +3511,6 @@ version = "0.4.7" description = "llama-index llms openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_llms_openai-0.4.7-py3-none-any.whl", hash = "sha256:3b8d9d3c1bcadc2cff09724de70f074f43eafd5b7048a91247c9a41b7cd6216d"}, {file = "llama_index_llms_openai-0.4.7.tar.gz", hash = "sha256:564af8ab39fb3f3adfeae73a59c0dca46c099ab844a28e725eee0c551d4869f8"}, @@ -3752,7 +3526,6 @@ version = "0.5.1" description = "llama-index multi-modal-llms openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_multi_modal_llms_openai-0.5.1-py3-none-any.whl", hash = "sha256:69bb9c310c323ce51038f0d40719f546d0d4e0853835a4f5cfa21f07dbb0b91e"}, {file = "llama_index_multi_modal_llms_openai-0.5.1.tar.gz", hash = "sha256:df3aff00c36023c5f8c49f972a325f71823ed0f4dd9cd479955d76afc146575f"}, @@ -3768,7 +3541,6 @@ version = "0.3.2" description = "llama-index program openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_program_openai-0.3.2-py3-none-any.whl", hash = "sha256:451829ae53e074e7b47dcc60a9dd155fcf9d1dcbc1754074bdadd6aab4ceb9aa"}, {file = "llama_index_program_openai-0.3.2.tar.gz", hash = "sha256:04c959a2e616489894bd2eeebb99500d6f1c17d588c3da0ddc75ebd3eb7451ee"}, @@ -3785,7 +3557,6 @@ version = "0.3.1" description = "llama-index question_gen openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_question_gen_openai-0.3.1-py3-none-any.whl", hash = "sha256:1ce266f6c8373fc8d884ff83a44dfbacecde2301785db7144872db51b8b99429"}, {file = "llama_index_question_gen_openai-0.3.1.tar.gz", hash = "sha256:5e9311b433cc2581ff8a531fa19fb3aa21815baff75aaacdef11760ac9522aa9"}, @@ -3802,7 +3573,6 @@ version = "0.4.11" description = "llama-index readers file integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_readers_file-0.4.11-py3-none-any.whl", hash = "sha256:e71192d8d6d0bf95131762da15fa205cf6e0cc248c90c76ee04d0fbfe160d464"}, {file = "llama_index_readers_file-0.4.11.tar.gz", hash = "sha256:1b21cb66d78dd5f60e8716607d9a47ccd81bb39106d459665be1ca7799e9597b"}, @@ -3825,7 +3595,6 @@ version = "0.4.0" description = "llama-index readers llama-parse integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_index_readers_llama_parse-0.4.0-py3-none-any.whl", hash = "sha256:574e48386f28d2c86c3f961ca4a4906910312f3400dd0c53014465bfbc6b32bf"}, {file = "llama_index_readers_llama_parse-0.4.0.tar.gz", hash = "sha256:e99ec56f4f8546d7fda1a7c1ae26162fb9acb7ebcac343b5abdb4234b4644e0f"}, @@ -3841,7 +3610,6 @@ version = "0.6.43" description = "Parse files into RAG-Optimized formats." optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "llama_parse-0.6.43-py3-none-any.whl", hash = "sha256:fe435309638c4fdec4fec31f97c5031b743c92268962d03b99bd76704f566c32"}, {file = "llama_parse-0.6.43.tar.gz", hash = "sha256:d88e91c97e37f77b2619111ef43c02b7da61125f821cf77f918996eb48200d78"}, @@ -3856,8 +3624,6 @@ version = "2.37.12" description = "Developer-friendly load testing framework" optional = true python-versions = ">=3.10" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "locust-2.37.12-py3-none-any.whl", hash = "sha256:7866567a3e38103fb5db096864701f99e9136617269c799158518ccbf1c98982"}, {file = "locust-2.37.12.tar.gz", hash = "sha256:e62fb5230cc279c87114e180facf429f0717074b68e08e5a46226419e6674ee6"}, @@ -3876,8 +3642,8 @@ psutil = ">=5.9.1" pywin32 = {version = "*", markers = "sys_platform == \"win32\""} pyzmq = ">=25.0.0" requests = [ - {version = ">=2.32.2", markers = "python_version > \"3.11\""}, {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, + {version = ">=2.32.2", markers = "python_version > \"3.11\""}, ] setuptools = ">=70.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -3890,8 +3656,6 @@ version = "1.25.1" description = "Locust Cloud" optional = true python-versions = ">=3.10" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "locust_cloud-1.25.1-py3-none-any.whl", hash = "sha256:0b9e562d2521a6efa9e2f97c2d6ef6d9807ca56868ed9c8d73827fe94f2b94f3"}, {file = "locust_cloud-1.25.1.tar.gz", hash = "sha256:f248e3929a2021a1ea178253b77c56e32ef882899f90bd7c6f762522abdbd769"}, @@ -3911,7 +3675,6 @@ version = "6.0.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8"}, {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082"}, @@ -4021,7 +3784,6 @@ version = "0.6.2" description = "A tool to determine the content type of a file with deep learning" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "magika-0.6.2-py3-none-any.whl", hash = "sha256:5ef72fbc07723029b3684ef81454bc224ac5f60986aa0fc5a28f4456eebcb5b2"}, {file = "magika-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9109309328a1553886c8ff36c2ee9a5e9cfd36893ad81b65bf61a57debdd9d0e"}, @@ -4033,9 +3795,9 @@ files = [ [package.dependencies] click = ">=8.1.7" numpy = [ - {version = ">=1.26", markers = "python_version == \"3.12\""}, - {version = ">=2.1.0", markers = "python_version >= \"3.13\""}, {version = ">=1.24", markers = "python_version < \"3.12\""}, + {version = ">=1.26", markers = "python_version >= \"3.12\" and python_version < \"3.13\""}, + {version = ">=2.1.0", markers = "python_version >= \"3.13\""}, ] onnxruntime = {version = ">=1.17.0", markers = "python_version > \"3.9\""} python-dotenv = ">=1.0.1" @@ -4046,7 +3808,6 @@ version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, @@ -4066,7 +3827,6 @@ version = "1.9.1" description = "Convert Word documents from docx to simple and clean HTML and Markdown" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "mammoth-1.9.1-py2.py3-none-any.whl", hash = "sha256:f0569bd640cee6c77a07e7c75c5dc10d745dc4dc95d530cfcbb0a5d9536d636c"}, {file = "mammoth-1.9.1.tar.gz", hash = "sha256:7924254ab8f03efe55fadc0fd5f7828db831190eb2679d63cb4372873e71c572"}, @@ -4081,7 +3841,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -4106,7 +3865,6 @@ version = "1.1.0" description = "Convert HTML to markdown." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "markdownify-1.1.0-py3-none-any.whl", hash = "sha256:32a5a08e9af02c8a6528942224c91b933b4bd2c7d078f9012943776fc313eeef"}, {file = "markdownify-1.1.0.tar.gz", hash = "sha256:449c0bbbf1401c5112379619524f33b63490a8fa479456d41de9dc9e37560ebd"}, @@ -4122,7 +3880,6 @@ version = "0.1.2" description = "Utility tool for converting various files to Markdown" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "markitdown-0.1.2-py3-none-any.whl", hash = "sha256:4881f0768794ffccb52d09dd86498813a6896ba9639b4fc15512817f56ed9d74"}, {file = "markitdown-0.1.2.tar.gz", hash = "sha256:85fe108a92bd18f317e75a36cf567a6fa812072612a898abf8c156d5d74c13c4"}, @@ -4158,7 +3915,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -4229,7 +3985,6 @@ version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, @@ -4249,7 +4004,6 @@ version = "1.4.2" description = "SQLAlchemy integration with the marshmallow (de)serialization library" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "marshmallow_sqlalchemy-1.4.2-py3-none-any.whl", hash = "sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40"}, {file = "marshmallow_sqlalchemy-1.4.2.tar.gz", hash = "sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c"}, @@ -4261,7 +4015,7 @@ SQLAlchemy = ">=1.4.40,<3.0" [package.extras] dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<5.0)", "tox"] -docs = ["furo (==2024.8.6)", "sphinx (==8.2.3) ; python_version >= \"3.11\"", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.10.0)"] +docs = ["furo (==2024.8.6)", "sphinx (==8.2.3)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.10.0)"] tests = ["pytest (<9)", "pytest-lazy-fixtures"] [[package]] @@ -4270,7 +4024,6 @@ version = "3.10.3" description = "Python plotting package" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7"}, {file = "matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb"}, @@ -4328,12 +4081,10 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] traitlets = "*" @@ -4344,7 +4095,6 @@ version = "1.10.1" description = "Model Context Protocol SDK" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "mcp-1.10.1-py3-none-any.whl", hash = "sha256:4d08301aefe906dce0fa482289db55ce1db831e3e67212e65b5e23ad8454b3c5"}, {file = "mcp-1.10.1.tar.gz", hash = "sha256:aaa0957d8307feeff180da2d9d359f2b801f35c0c67f1882136239055ef034c2"}, @@ -4375,7 +4125,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -4387,7 +4136,6 @@ version = "1.9.1" description = "Python Client SDK for the Mistral AI API." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "mistralai-1.9.1-py3-none-any.whl", hash = "sha256:250ec26534db6f4a4d5e6292b0801a64da2ab1f0d4c63a20d8ce27e3a427e402"}, {file = "mistralai-1.9.1.tar.gz", hash = "sha256:89eb1d48e9555c8289c02ddea966115eba0516355731726ea0a24eabb42f8419"}, @@ -4401,7 +4149,7 @@ python-dateutil = ">=2.8.2" typing-inspection = ">=0.4.0" [package.extras] -agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0) ; python_version >= \"3.10\""] +agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0)"] gcp = ["google-auth (>=2.27.0)", "requests (>=2.32.3)"] [[package]] @@ -4410,7 +4158,6 @@ version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -4419,7 +4166,7 @@ files = [ [package.extras] develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] docs = ["sphinx"] -gmpy = ["gmpy2 (>=2.1.0a4) ; platform_python_implementation != \"PyPy\""] +gmpy = ["gmpy2 (>=2.1.0a4)"] tests = ["pytest (>=4.6)"] [[package]] @@ -4428,8 +4175,6 @@ version = "1.1.1" description = "MessagePack serializer" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, @@ -4498,7 +4243,6 @@ version = "6.6.3" description = "multidict implementation" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817"}, {file = "multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140"}, @@ -4621,7 +4365,6 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -4633,7 +4376,6 @@ version = "1.6.0" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" -groups = ["main", "dev"] files = [ {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, @@ -4645,7 +4387,6 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -4665,7 +4406,6 @@ version = "3.9.1" description = "Natural Language Toolkit" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, @@ -4691,8 +4431,6 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\" or extra == \"desktop\"" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -4704,7 +4442,6 @@ version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, @@ -4769,7 +4506,6 @@ version = "1.22.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = ">=3.10" -groups = ["main"] files = [ {file = "onnxruntime-1.22.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:80e7f51da1f5201c1379b8d6ef6170505cd800e40da216290f5e06be01aadf95"}, {file = "onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89ddfdbbdaf7e3a59515dee657f6515601d55cb21a0f0f48c81aefc54ff1b73"}, @@ -4805,7 +4541,6 @@ version = "1.93.3" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "openai-1.93.3-py3-none-any.whl", hash = "sha256:41aaa7594c7d141b46eed0a58dcd75d20edcc809fdd2c931ecbb4957dc98a892"}, {file = "openai-1.93.3.tar.gz", hash = "sha256:488b76399238c694af7e4e30c58170ea55e6f65038ab27dbe95b5077a00f8af8"}, @@ -4833,7 +4568,6 @@ version = "1.30.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09"}, {file = "opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240"}, @@ -4849,7 +4583,6 @@ version = "1.30.0" description = "OpenTelemetry Collector Exporters" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp-1.30.0-py3-none-any.whl", hash = "sha256:44e11054ec571ccfed73a83c6429dee5d334d061d0e0572e3160d6de97156dbc"}, {file = "opentelemetry_exporter_otlp-1.30.0.tar.gz", hash = "sha256:da7769f95cd5be5b09dd4188ac153a33709eda652217f2d10aed6518c8e60f0d"}, @@ -4865,7 +4598,6 @@ version = "1.30.0" description = "OpenTelemetry Protobuf encoding" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_common-1.30.0-py3-none-any.whl", hash = "sha256:5468007c81aa9c44dc961ab2cf368a29d3475977df83b4e30aeed42aa7bc3b38"}, {file = "opentelemetry_exporter_otlp_proto_common-1.30.0.tar.gz", hash = "sha256:ddbfbf797e518411857d0ca062c957080279320d6235a279f7b64ced73c13897"}, @@ -4880,7 +4612,6 @@ version = "1.30.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0-py3-none-any.whl", hash = "sha256:2906bcae3d80acc54fd1ffcb9e44d324e8631058b502ebe4643ca71d1ff30830"}, {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0.tar.gz", hash = "sha256:d0f10f0b9b9a383b7d04a144d01cb280e70362cccc613987e234183fd1f01177"}, @@ -4901,7 +4632,6 @@ version = "1.30.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_http-1.30.0-py3-none-any.whl", hash = "sha256:9578e790e579931c5ffd50f1e6975cbdefb6a0a0a5dea127a6ae87df10e0a589"}, {file = "opentelemetry_exporter_otlp_proto_http-1.30.0.tar.gz", hash = "sha256:c3ae75d4181b1e34a60662a6814d0b94dd33b628bee5588a878bed92cee6abdc"}, @@ -4922,7 +4652,6 @@ version = "0.51b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_instrumentation-0.51b0-py3-none-any.whl", hash = "sha256:c6de8bd26b75ec8b0e54dff59e198946e29de6a10ec65488c357d4b34aa5bdcf"}, {file = "opentelemetry_instrumentation-0.51b0.tar.gz", hash = "sha256:4ca266875e02f3988536982467f7ef8c32a38b8895490ddce9ad9604649424fa"}, @@ -4940,7 +4669,6 @@ version = "0.51b0" description = "OpenTelemetry requests instrumentation" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_instrumentation_requests-0.51b0-py3-none-any.whl", hash = "sha256:0723aaafaeb2a825723f31c0bf644f9642377046063d1a52fc86571ced87feac"}, {file = "opentelemetry_instrumentation_requests-0.51b0.tar.gz", hash = "sha256:e7f4bd3ffcab6ebcce8a1c652af218e050354c8e7cac2c34814292d4de75167a"}, @@ -4961,7 +4689,6 @@ version = "0.51b0" description = "OpenTelemetry SQLAlchemy instrumentation" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_instrumentation_sqlalchemy-0.51b0-py3-none-any.whl", hash = "sha256:5ff4816228b496aef1511149e2b17a25e0faacec4d5eb65bf18a9964af40f1af"}, {file = "opentelemetry_instrumentation_sqlalchemy-0.51b0.tar.gz", hash = "sha256:dbfe95b69006017f903dda194606be458d54789e6b3419d37161fb8861bb98a5"}, @@ -4983,7 +4710,6 @@ version = "1.30.0" description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_proto-1.30.0-py3-none-any.whl", hash = "sha256:c6290958ff3ddacc826ca5abbeb377a31c2334387352a259ba0df37c243adc11"}, {file = "opentelemetry_proto-1.30.0.tar.gz", hash = "sha256:afe5c9c15e8b68d7c469596e5b32e8fc085eb9febdd6fb4e20924a93a0389179"}, @@ -4998,7 +4724,6 @@ version = "1.30.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091"}, {file = "opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18"}, @@ -5015,7 +4740,6 @@ version = "0.51b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae"}, {file = "opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47"}, @@ -5031,7 +4755,6 @@ version = "0.51b0" description = "Web util for OpenTelemetry" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "opentelemetry_util_http-0.51b0-py3-none-any.whl", hash = "sha256:0561d7a6e9c422b9ef9ae6e77eafcfcd32a2ab689f5e801475cbb67f189efa20"}, {file = "opentelemetry_util_http-0.51b0.tar.gz", hash = "sha256:05edd19ca1cc3be3968b1e502fd94816901a365adbeaab6b6ddb974384d3a0b9"}, @@ -5043,8 +4766,6 @@ version = "3.10.18" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "platform_python_implementation != \"PyPy\" and (extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\")" files = [ {file = "orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402"}, {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c"}, @@ -5126,7 +4847,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -5138,7 +4858,6 @@ version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -5186,9 +4905,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -5225,7 +4944,6 @@ version = "3.5.1" description = "SSH2 protocol library" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61"}, {file = "paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822"}, @@ -5237,8 +4955,8 @@ cryptography = ">=3.3" pynacl = ">=1.5" [package.extras] -all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] -gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] invoke = ["invoke (>=2.0)"] [[package]] @@ -5247,12 +4965,10 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] @@ -5264,7 +4980,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -5276,7 +4991,6 @@ version = "3.3.1" description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f"}, {file = "pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177"}, @@ -5293,7 +5007,6 @@ version = "20250506" description = "PDF parser and analyzer" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pdfminer_six-20250506-py3-none-any.whl", hash = "sha256:d81ad173f62e5f841b53a8ba63af1a4a355933cfc0ffabd608e568b9193909e3"}, {file = "pdfminer_six-20250506.tar.gz", hash = "sha256:b03cc8df09cf3c7aba8246deae52e0bca7ebb112a38895b5e1d4f5dd2b8ca2e7"}, @@ -5304,7 +5017,7 @@ charset-normalizer = ">=2.0.0" cryptography = ">=36.0.0" [package.extras] -dev = ["atheris ; python_version < \"3.12\"", "black", "mypy (==0.931)", "nox", "pytest"] +dev = ["atheris", "black", "mypy (==0.931)", "nox", "pytest"] docs = ["sphinx", "sphinx-argparse"] image = ["Pillow"] @@ -5314,12 +5027,10 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\"", dev = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or extra == \"dev\" or extra == \"all\""} [package.dependencies] ptyprocess = ">=0.5" @@ -5330,8 +5041,6 @@ version = "1.31.2" description = "PostgreSQL interface library" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, @@ -5347,8 +5056,6 @@ version = "0.2.5" description = "pgvector support for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "pgvector-0.2.5-py2.py3-none-any.whl", hash = "sha256:5e5e93ec4d3c45ab1fa388729d56c602f6966296e19deee8878928c6d567e41b"}, ] @@ -5362,7 +5069,6 @@ version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, @@ -5451,7 +5157,7 @@ docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions ; python_version < \"3.10\""] +typing = ["typing-extensions"] xmp = ["defusedxml"] [[package]] @@ -5460,8 +5166,6 @@ version = "7.3.0" description = "Pinecone client and SDK" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "pinecone-7.3.0-py3-none-any.whl", hash = "sha256:315b8fef20320bef723ecbb695dec0aafa75d8434d86e01e5a0e85933e1009a8"}, {file = "pinecone-7.3.0.tar.gz", hash = "sha256:307edc155621d487c20dc71b76c3ad5d6f799569ba42064190d03917954f9a7b"}, @@ -5476,13 +5180,13 @@ pinecone-plugin-interface = ">=0.0.7,<0.0.8" python-dateutil = ">=2.5.3" typing-extensions = ">=3.7.4" urllib3 = [ - {version = ">=1.26.5", markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, {version = ">=1.26.0", markers = "python_version >= \"3.8\" and python_version < \"3.12\""}, + {version = ">=1.26.5", markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, ] [package.extras] asyncio = ["aiohttp (>=3.9.0)", "aiohttp-retry (>=2.9.1,<3.0.0)"] -grpc = ["googleapis-common-protos (>=1.66.0)", "grpcio (>=1.44.0) ; python_version >= \"3.8\" and python_version < \"3.11\"", "grpcio (>=1.59.0) ; python_version >= \"3.11\" and python_version < \"4.0\"", "grpcio (>=1.68.0) ; python_version >= \"3.13\" and python_version < \"4.0\"", "lz4 (>=3.1.3)", "protobuf (>=5.29,<6.0)", "protoc-gen-openapiv2 (>=0.0.1,<0.0.2)"] +grpc = ["googleapis-common-protos (>=1.66.0)", "grpcio (>=1.44.0)", "grpcio (>=1.59.0)", "grpcio (>=1.68.0)", "lz4 (>=3.1.3)", "protobuf (>=5.29,<6.0)", "protoc-gen-openapiv2 (>=0.0.1,<0.0.2)"] [[package]] name = "pinecone-plugin-assistant" @@ -5490,8 +5194,6 @@ version = "1.7.0" description = "Assistant plugin for Pinecone SDK" optional = true python-versions = "<4.0,>=3.9" -groups = ["main"] -markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "pinecone_plugin_assistant-1.7.0-py3-none-any.whl", hash = "sha256:864cb8e7930588e6c2da97c6d44f0240969195f43fa303c5db76cbc12bf903a5"}, {file = "pinecone_plugin_assistant-1.7.0.tar.gz", hash = "sha256:e26e3ba10a8b71c3da0d777cff407668022e82963c4913d0ffeb6c552721e482"}, @@ -5507,8 +5209,6 @@ version = "0.0.7" description = "Plugin interface for the Pinecone python client" optional = true python-versions = "<4.0,>=3.8" -groups = ["main"] -markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "pinecone_plugin_interface-0.0.7-py3-none-any.whl", hash = "sha256:875857ad9c9fc8bbc074dbe780d187a2afd21f5bfe0f3b08601924a61ef1bba8"}, {file = "pinecone_plugin_interface-0.0.7.tar.gz", hash = "sha256:b8e6675e41847333aa13923cc44daa3f85676d7157324682dc1640588a982846"}, @@ -5520,7 +5220,6 @@ version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, @@ -5537,12 +5236,10 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["main", "dev", "dev,tests"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] dev = ["pre-commit", "tox"] @@ -5554,8 +5251,6 @@ version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, @@ -5574,7 +5269,6 @@ version = "3.16.0" description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "prettytable-3.16.0-py3-none-any.whl", hash = "sha256:b5eccfabb82222f5aa46b798ff02a8452cf530a352c31bddfa29be41242863aa"}, {file = "prettytable-3.16.0.tar.gz", hash = "sha256:3c64b31719d961bf69c9a7e03d0c1e477320906a98da63952bc6698d6164ff57"}, @@ -5592,7 +5286,6 @@ version = "3.0.51" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, @@ -5607,7 +5300,6 @@ version = "0.3.2" description = "Accelerated property cache" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, @@ -5715,7 +5407,6 @@ version = "5.29.5" description = "" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079"}, {file = "protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc"}, @@ -5736,7 +5427,6 @@ version = "7.0.0" description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, @@ -5749,7 +5439,6 @@ files = [ {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, ] -markers = {main = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\""} [package.extras] dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] @@ -5761,8 +5450,6 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, {file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"}, @@ -5770,7 +5457,6 @@ files = [ {file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"}, {file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"}, {file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"}, - {file = "psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2"}, {file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"}, {file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"}, {file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"}, @@ -5782,8 +5468,6 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, @@ -5832,7 +5516,6 @@ files = [ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, - {file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, @@ -5861,12 +5544,10 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\"", dev = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or extra == \"dev\" or extra == \"all\""} [[package]] name = "pure-eval" @@ -5874,12 +5555,10 @@ version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] tests = ["pytest"] @@ -5890,8 +5569,6 @@ version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -5903,8 +5580,6 @@ version = "0.4.2" description = "A collection of ASN.1-based protocols modules" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, @@ -5919,12 +5594,10 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -markers = {dev = "implementation_name == \"pypy\""} [[package]] name = "pydantic" @@ -5932,7 +5605,6 @@ version = "2.11.7" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, @@ -5947,7 +5619,7 @@ typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] +timezone = ["tzdata"] [[package]] name = "pydantic-core" @@ -5955,7 +5627,6 @@ version = "2.33.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, @@ -6067,7 +5738,6 @@ version = "2.10.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796"}, {file = "pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee"}, @@ -6091,8 +5761,6 @@ version = "3.4.0" description = "passive checker of Python programs" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, @@ -6104,7 +5772,6 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -6119,7 +5786,6 @@ version = "3.8.0" description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, @@ -6131,7 +5797,6 @@ version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, @@ -6158,7 +5823,6 @@ version = "3.2.3" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, @@ -6173,7 +5837,6 @@ version = "5.7.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "pypdf-5.7.0-py3-none-any.whl", hash = "sha256:203379453439f5b68b7a1cd43cdf4c5f7a02b84810cefa7f93a47b350aaaba48"}, {file = "pypdf-5.7.0.tar.gz", hash = "sha256:68c92f2e1aae878bab1150e74447f31ab3848b1c0a6f8becae9f0b1904460b6f"}, @@ -6196,7 +5859,6 @@ version = "1.9.0" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, ] @@ -6207,8 +5869,6 @@ version = "3.5.4" description = "A python implementation of GNU readline." optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "sys_platform == \"win32\"" files = [ {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, @@ -6223,8 +5883,6 @@ version = "1.1.403" description = "Command line wrapper for pyright" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3"}, {file = "pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104"}, @@ -6245,7 +5903,6 @@ version = "1.0.8" description = "Pusher websocket client for python, based on Erik Kulyk's PythonPusherClient" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "Pysher-1.0.8.tar.gz", hash = "sha256:7849c56032b208e49df67d7bd8d49029a69042ab0bb45b2ed59fa08f11ac5988"}, ] @@ -6260,12 +5917,10 @@ version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev", "dev,tests"] files = [ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} @@ -6285,8 +5940,6 @@ version = "0.24.0" description = "Pytest support for asyncio" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, @@ -6305,7 +5958,6 @@ version = "1.5.0" description = "A pytest plugin to report test results as JSON files" optional = false python-versions = "*" -groups = ["dev,tests"] files = [ {file = "pytest-json-report-1.5.0.tar.gz", hash = "sha256:2dde3c647851a19b5f3700729e8310a6e66efb2077d674f27ddea3d34dc615de"}, {file = "pytest_json_report-1.5.0-py3-none-any.whl", hash = "sha256:9897b68c910b12a2e48dd849f9a284b2c79a732a8a9cb398452ddd23d3c8c325"}, @@ -6321,7 +5973,6 @@ version = "3.1.1" description = "pytest plugin for test session metadata" optional = false python-versions = ">=3.8" -groups = ["dev,tests"] files = [ {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, @@ -6339,7 +5990,6 @@ version = "3.14.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, @@ -6357,8 +6007,6 @@ version = "1.3.0" description = "pytest plugin to run your tests in a specific order" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pytest_order-1.3.0-py3-none-any.whl", hash = "sha256:2cd562a21380345dd8d5774aa5fd38b7849b6ee7397ca5f6999bbe6e89f07f6e"}, {file = "pytest_order-1.3.0.tar.gz", hash = "sha256:51608fec3d3ee9c0adaea94daa124a5c4c1d2bb99b00269f098f414307f23dde"}, @@ -6373,7 +6021,6 @@ version = "7.3.2" description = "Advanced Python dictionaries with dot notation access" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "python_box-7.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d136163294fd61a1554db7dd203f2e3035064798d30c17d67d948f0de5c572de"}, {file = "python_box-7.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d72e96547d8e2c2c333909826e9fae338d9a7e4cde07d5c6058cdd468432c0"}, @@ -6400,7 +6047,7 @@ msgpack = ["msgpack"] pyyaml = ["PyYAML"] ruamel-yaml = ["ruamel.yaml (>=0.17)"] toml = ["toml"] -tomli = ["tomli ; python_version < \"3.11\"", "tomli-w"] +tomli = ["tomli", "tomli-w"] yaml = ["ruamel.yaml (>=0.17)"] [[package]] @@ -6409,7 +6056,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -6424,7 +6070,6 @@ version = "1.1.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"}, {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"}, @@ -6439,8 +6084,6 @@ version = "4.12.2" description = "Engine.IO server and client for Python" optional = true python-versions = ">=3.6" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "python_engineio-4.12.2-py3-none-any.whl", hash = "sha256:8218ab66950e179dfec4b4bbb30aecf3f5d86f5e58e6fc1aa7fde2c698b2804f"}, {file = "python_engineio-4.12.2.tar.gz", hash = "sha256:e7e712ffe1be1f6a05ee5f951e72d434854a32fcfc7f6e4d9d3cae24ec70defa"}, @@ -6460,7 +6103,6 @@ version = "0.0.19" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"}, {file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"}, @@ -6472,7 +6114,6 @@ version = "1.0.2" description = "Create, read, and update PowerPoint 2007+ (.pptx) files." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba"}, {file = "python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095"}, @@ -6490,8 +6131,6 @@ version = "5.13.0" description = "Socket.IO server and client for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "python_socketio-5.13.0-py3-none-any.whl", hash = "sha256:51f68d6499f2df8524668c24bcec13ba1414117cfb3a90115c559b601ab10caf"}, {file = "python_socketio-5.13.0.tar.gz", hash = "sha256:ac4e19a0302ae812e23b712ec8b6427ca0521f7c582d6abb096e36e24a263029"}, @@ -6514,7 +6153,6 @@ version = "2023.4" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pytz-2023.4-py2.py3-none-any.whl", hash = "sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a"}, {file = "pytz-2023.4.tar.gz", hash = "sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40"}, @@ -6526,7 +6164,6 @@ version = "310" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, {file = "pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d"}, @@ -6545,7 +6182,6 @@ files = [ {file = "pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a"}, {file = "pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475"}, ] -markers = {main = "sys_platform == \"win32\" and (extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\" or extra == \"dev\")", dev = "platform_python_implementation != \"PyPy\" and sys_platform == \"win32\""} [[package]] name = "pyyaml" @@ -6553,7 +6189,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -6616,7 +6251,6 @@ version = "27.0.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pyzmq-27.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b973ee650e8f442ce482c1d99ca7ab537c69098d53a3d046676a484fd710c87a"}, {file = "pyzmq-27.0.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:661942bc7cd0223d569d808f2e5696d9cc120acc73bf3e88a1f1be7ab648a7e4"}, @@ -6698,7 +6332,6 @@ files = [ {file = "pyzmq-27.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:096af9e133fec3a72108ddefba1e42985cb3639e9de52cfd336b6fc23aa083e9"}, {file = "pyzmq-27.0.0.tar.gz", hash = "sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf"}, ] -markers = {main = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\""} [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} @@ -6709,7 +6342,6 @@ version = "2.1.0" description = "Python library to build pretty command line user prompts ⭐️" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, @@ -6724,8 +6356,6 @@ version = "6.2.0" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"redis\" or extra == \"all\"" files = [ {file = "redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e"}, {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, @@ -6745,7 +6375,6 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -6762,7 +6391,6 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -6866,7 +6494,6 @@ version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, @@ -6888,8 +6515,6 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -6904,7 +6529,6 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["main"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -6924,7 +6548,6 @@ version = "0.26.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37"}, {file = "rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0"}, @@ -7078,8 +6701,6 @@ version = "4.9.1" description = "Pure-Python RSA implementation" optional = true python-versions = "<4,>=3.6" -groups = ["main"] -markers = "extra == \"google\"" files = [ {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"}, {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"}, @@ -7094,8 +6715,6 @@ version = "0.11.3" description = "An Amazon S3 Transfer Manager" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"bedrock\"" files = [ {file = "s3transfer-0.11.3-py3-none-any.whl", hash = "sha256:ca855bdeb885174b5ffa95b9913622459d4ad8e331fc98eb01e6d5eb6a30655d"}, {file = "s3transfer-0.11.3.tar.gz", hash = "sha256:edae4977e3a122445660c7c114bba949f9d191bae3b34a096f18a1c8c354527a"}, @@ -7113,8 +6732,6 @@ version = "1.4.6" description = "An implementation of the SCRAM protocol." optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "scramp-1.4.6-py3-none-any.whl", hash = "sha256:a0cf9d2b4624b69bac5432dd69fecfc55a542384fe73c3a23ed9b138cda484e1"}, {file = "scramp-1.4.6.tar.gz", hash = "sha256:fe055ebbebf4397b9cb323fcc4b299f219cd1b03fd673ca40c97db04ac7d107e"}, @@ -7129,7 +6746,6 @@ version = "3.0.4" description = "Python helper for Semantic Versioning (https://semver.org)" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, @@ -7141,7 +6757,6 @@ version = "2.19.1" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "sentry_sdk-2.19.1-py2.py3-none-any.whl", hash = "sha256:b056e04b766f805fdf0aa620482cafe2ff000c8fcb51cb266cdb90873e93837b"}, {file = "sentry_sdk-2.19.1.tar.gz", hash = "sha256:6ad8507457a379b72f832aca55787b21e7391751892faef1fd8bace350aa5e17"}, @@ -7197,7 +6812,6 @@ version = "70.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc"}, {file = "setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5"}, @@ -7205,7 +6819,7 @@ files = [ [package.extras] 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) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "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 ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +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.10.0)", "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.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" @@ -7213,7 +6827,6 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -7225,8 +6838,6 @@ version = "1.1.0" description = "Simple WebSocket server and client for Python" optional = true python-versions = ">=3.6" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c"}, {file = "simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"}, @@ -7245,7 +6856,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -7257,7 +6867,6 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -7269,7 +6878,6 @@ version = "2.7" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, @@ -7281,7 +6889,6 @@ version = "2.0.41" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, @@ -7377,7 +6984,6 @@ version = "0.7.0" description = "JSON type with nested change tracking for SQLAlchemy" optional = false python-versions = ">= 3.6" -groups = ["main"] files = [ {file = "sqlalchemy-json-0.7.0.tar.gz", hash = "sha256:620d0b26f648f21a8fa9127df66f55f83a5ab4ae010e5397a5c6989a08238561"}, {file = "sqlalchemy_json-0.7.0-py3-none-any.whl", hash = "sha256:27881d662ca18363a4ac28175cc47ea2a6f2bef997ae1159c151026b741818e6"}, @@ -7395,7 +7001,6 @@ version = "0.41.2" description = "Various utility functions for SQLAlchemy." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "SQLAlchemy-Utils-0.41.2.tar.gz", hash = "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990"}, {file = "SQLAlchemy_Utils-0.41.2-py3-none-any.whl", hash = "sha256:85cf3842da2bf060760f955f8467b87983fb2e30f1764fd0e24a48307dc8ec6e"}, @@ -7413,8 +7018,8 @@ intervals = ["intervals (>=0.7.1)"] password = ["passlib (>=1.6,<2.0)"] pendulum = ["pendulum (>=2.0.5)"] phone = ["phonenumbers (>=5.9.2)"] -test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo ; python_version < \"3.9\"", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] -test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo ; python_version < \"3.9\"", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] timezone = ["python-dateutil"] url = ["furl (>=0.4.1)"] @@ -7424,7 +7029,6 @@ version = "0.1.7a2" description = "" optional = false python-versions = "*" -groups = ["sqlite"] files = [ {file = "sqlite_vec-0.1.7a2-py3-none-macosx_10_6_x86_64.whl", hash = "sha256:a08dd9396d494ac8970ba519a3931410f08c0c5eeadd0e1a2e02053789f6c877"}, {file = "sqlite_vec-0.1.7a2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b98d7af645d28c0b5c844bf1d99fe2103fe1320fe2bbf36d0713f0b36764fdcb"}, @@ -7439,7 +7043,6 @@ version = "0.0.16" description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." optional = false python-versions = ">=3.7,<4.0" -groups = ["main"] files = [ {file = "sqlmodel-0.0.16-py3-none-any.whl", hash = "sha256:b972f5d319580d6c37ecc417881f6ec4d1ad3ed3583d0ac0ed43234a28bf605a"}, {file = "sqlmodel-0.0.16.tar.gz", hash = "sha256:966656f18a8e9a2d159eb215b07fb0cf5222acfae3362707ca611848a8a06bd1"}, @@ -7455,7 +7058,6 @@ version = "2.4.1" description = "SSE plugin for Starlette" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "sse_starlette-2.4.1-py3-none-any.whl", hash = "sha256:08b77ea898ab1a13a428b2b6f73cfe6d0e607a7b4e15b9bb23e4a37b087fd39a"}, {file = "sse_starlette-2.4.1.tar.gz", hash = "sha256:7c8a800a1ca343e9165fc06bbda45c78e4c6166320707ae30b416c42da070926"}, @@ -7476,12 +7078,10 @@ version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] asttokens = ">=2.1.0" @@ -7497,7 +7097,6 @@ version = "0.46.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, @@ -7515,7 +7114,6 @@ version = "0.0.26" description = "A simple library to convert rtf to text" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb"}, {file = "striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa"}, @@ -7527,7 +7125,6 @@ version = "25.4.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, @@ -7542,7 +7139,6 @@ version = "1.14.0" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"}, {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"}, @@ -7560,7 +7156,6 @@ version = "0.7.9" description = "Python wrapper for the Tavily API" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "tavily_python-0.7.9-py3-none-any.whl", hash = "sha256:6d70ea86e2ccba061d0ea98c81922784a01c186960304d44436304f114f22372"}, {file = "tavily_python-0.7.9.tar.gz", hash = "sha256:61aa13ca89e2e40d645042c8d27afc478b27846fb79bb21d4f683ed28f173dc7"}, @@ -7577,7 +7172,6 @@ version = "8.5.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, @@ -7593,7 +7187,6 @@ version = "0.9.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, @@ -7641,8 +7234,6 @@ version = "6.2.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "tokenize_rt-6.2.0-py2.py3-none-any.whl", hash = "sha256:a152bf4f249c847a66497a4a95f63376ed68ac6abf092a2f7cfb29d044ecff44"}, {file = "tokenize_rt-6.2.0.tar.gz", hash = "sha256:8439c042b330c553fdbe1758e4a05c0ed460dbbbb24a606f11f0dee75da4cad6"}, @@ -7654,8 +7245,6 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["main"] -markers = "python_version == \"3.10\"" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -7667,8 +7256,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "dev,tests"] -markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -7710,7 +7297,6 @@ version = "6.5.1" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7"}, {file = "tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6"}, @@ -7732,7 +7318,6 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -7754,12 +7339,10 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, ] -markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] @@ -7771,7 +7354,6 @@ version = "0.15.4" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "typer-0.15.4-py3-none-any.whl", hash = "sha256:eb0651654dcdea706780c466cf06d8f174405a659ffff8f163cfbfee98c0e173"}, {file = "typer-0.15.4.tar.gz", hash = "sha256:89507b104f9b6a0730354f27c39fae5b63ccd0c95b1ce1f1a6ba0cfd329997c3"}, @@ -7789,12 +7371,10 @@ version = "4.14.1" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "dev", "dev,tests", "sqlite"] files = [ {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, ] -markers = {dev = "python_version < \"3.12\"", "dev,tests" = "python_version == \"3.10\""} [[package]] name = "typing-inspect" @@ -7802,7 +7382,6 @@ version = "0.9.0" description = "Runtime inspection utilities for typing module." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, @@ -7818,7 +7397,6 @@ version = "0.4.1" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, @@ -7833,7 +7411,6 @@ version = "2025.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" -groups = ["main"] files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, @@ -7845,7 +7422,6 @@ version = "5.3.1" description = "tzinfo object for the local timezone" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d"}, {file = "tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd"}, @@ -7863,14 +7439,13 @@ version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -7881,7 +7456,6 @@ version = "0.24.0.post1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, @@ -7893,7 +7467,7 @@ h11 = ">=0.8" typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] -standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "uvloop" @@ -7901,8 +7475,6 @@ version = "0.21.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = true python-versions = ">=3.8.0" -groups = ["main"] -markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, @@ -7954,8 +7526,6 @@ version = "20.31.2" description = "Virtual Python Environment builder" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, @@ -7968,7 +7538,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "watchfiles" @@ -7976,8 +7546,6 @@ version = "1.1.0" description = "Simple, modern and high performance file watching and code reload in python." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc"}, {file = "watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df"}, @@ -8096,7 +7664,6 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" -groups = ["main", "dev"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -8108,7 +7675,6 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" -groups = ["main"] 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"}, @@ -8125,8 +7691,6 @@ version = "15.0.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"google\" or extra == \"external-tools\"" files = [ {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"}, {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"}, @@ -8205,8 +7769,6 @@ version = "3.1.3" description = "The comprehensive WSGI web application library." optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -8224,8 +7786,6 @@ version = "1.4.0" description = "Wikipedia API for Python" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"tests\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2"}, ] @@ -8240,7 +7800,6 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -8329,8 +7888,6 @@ version = "1.2.0" description = "WebSockets state-machine based protocol implementation" optional = true python-versions = ">=3.7.0" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, @@ -8345,7 +7902,6 @@ version = "3.2.5" description = "A Python module for creating Excel XLSX files." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "xlsxwriter-3.2.5-py3-none-any.whl", hash = "sha256:4f4824234e1eaf9d95df9a8fe974585ff91d0f5e3d3f12ace5b71e443c1c6abd"}, {file = "xlsxwriter-3.2.5.tar.gz", hash = "sha256:7e88469d607cdc920151c0ab3ce9cf1a83992d4b7bc730c5ffdd1a12115a7dbe"}, @@ -8357,7 +7913,6 @@ version = "1.20.1" description = "Yet another URL library" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, @@ -8476,14 +8031,13 @@ version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -8496,8 +8050,6 @@ version = "5.1" description = "Very basic event publishing system" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zope_event-5.1-py3-none-any.whl", hash = "sha256:53de8f0e9f61dc0598141ac591f49b042b6d74784dab49971b9cc91d0f73a7df"}, {file = "zope_event-5.1.tar.gz", hash = "sha256:a153660e0c228124655748e990396b9d8295d6e4f546fa1b34f3319e1c666e7f"}, @@ -8516,8 +8068,6 @@ version = "7.2" description = "Interfaces for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -8572,8 +8122,6 @@ version = "0.23.0" description = "Zstandard bindings for Python" optional = true python-versions = ">=3.8" -groups = ["main"] -markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"}, {file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"}, @@ -8696,6 +8244,6 @@ server = ["fastapi", "uvicorn"] tests = ["wikipedia"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = "<3.14,>=3.10" -content-hash = "278abdf8da256eed6451ab7a667b7f039773bb48c042b90983b82399b902ed03" +content-hash = "f7c0b095785d1e7363377731f8732075bd8ef01280490836bf651bb0587494b4" diff --git a/pyproject.toml b/pyproject.toml index ab4f6479..eecda66f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ llama-index = "^0.12.2" llama-index-embeddings-openai = "^0.3.1" e2b-code-interpreter = {version = "^1.0.3", optional = true} anthropic = "^0.49.0" -letta_client = "^0.1.219" +letta_client = "^0.1.220" openai = "^1.60.0" opentelemetry-api = "1.30.0" opentelemetry-sdk = "1.30.0" diff --git a/tests/test_sources.py b/tests/test_sources.py index cb956f6c..a3f49c82 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -995,7 +995,7 @@ def test_agent_open_file(disable_pinecone, client: LettaSDKClient, agent_state: file_metadata = upload_file_and_wait(client, source.id, file_path) # Basic test open_file function - closed_files = client.agents.open_file(agent_id=agent_state.id, file_id=file_metadata.id) + closed_files = client.agents.files.open(agent_id=agent_state.id, file_id=file_metadata.id) assert len(closed_files) == 0 @@ -1012,10 +1012,10 @@ def test_agent_close_file(disable_pinecone, client: LettaSDKClient, agent_state: file_metadata = upload_file_and_wait(client, source.id, file_path) # First open the file - client.agents.open_file(agent_id=agent_state.id, file_id=file_metadata.id) + client.agents.files.open(agent_id=agent_state.id, file_id=file_metadata.id) # Test close_file function - client.agents.close_file(agent_id=agent_state.id, file_id=file_metadata.id) + client.agents.files.close(agent_id=agent_state.id, file_id=file_metadata.id) # Result can be None or any type based on the signature # Just verify the function executes without error @@ -1037,10 +1037,10 @@ def test_agent_close_all_open_files(disable_pinecone, client: LettaSDKClient, ag file_metadata = upload_file_and_wait(client, source.id, file_path) file_metadatas.append(file_metadata) # Open each file - client.agents.open_file(agent_id=agent_state.id, file_id=file_metadata.id) + client.agents.files.open(agent_id=agent_state.id, file_id=file_metadata.id) # Test close_all_open_files function - result = client.agents.close_all_open_files(agent_id=agent_state.id) + result = client.agents.files.close_all(agent_id=agent_state.id) # Verify result is a list of strings assert isinstance(result, list), f"Expected list, got {type(result)}" From 694a6d2371271d947588886cbf352290a453bdb6 Mon Sep 17 00:00:00 2001 From: Kian Jones <11655409+kianjones9@users.noreply.github.com> Date: Thu, 24 Jul 2025 10:52:51 -0700 Subject: [PATCH 03/24] fix(ci): test_managers test was flaky on SQLite (#3520) --- tests/test_managers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_managers.py b/tests/test_managers.py index bc2b7c57..a6253053 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -2133,6 +2133,8 @@ async def test_list_agents_query_text_pagination(server: SyncServer, default_use actor=default_user, ) + await asyncio.sleep(0.1) + agent2 = await server.agent_manager.create_agent_async( agent_create=CreateAgent( name="Search Agent Two", @@ -2145,6 +2147,8 @@ async def test_list_agents_query_text_pagination(server: SyncServer, default_use actor=default_user, ) + await asyncio.sleep(0.1) + agent3 = await server.agent_manager.create_agent_async( agent_create=CreateAgent( name="Different Agent", From 410cb24ba9f9516efdcaa752ddc45f17de0c09f7 Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 24 Jul 2025 10:53:13 -0700 Subject: [PATCH 04/24] feat: Add timeout to file processing (#3513) --- letta/schemas/enums.py | 4 ++ letta/server/rest_api/routers/v1/sources.py | 26 +++++++++ letta/settings.py | 4 ++ tests/test_sources.py | 65 +++++++++++++++++++++ 4 files changed, 99 insertions(+) diff --git a/letta/schemas/enums.py b/letta/schemas/enums.py index a76178f9..fba439cb 100644 --- a/letta/schemas/enums.py +++ b/letta/schemas/enums.py @@ -102,6 +102,10 @@ class FileProcessingStatus(str, Enum): COMPLETED = "completed" ERROR = "error" + def is_terminal_state(self) -> bool: + """Check if the processing status is in a terminal state (completed or error).""" + return self in (FileProcessingStatus.COMPLETED, FileProcessingStatus.ERROR) + class ToolType(str, Enum): CUSTOM = "custom" diff --git a/letta/server/rest_api/routers/v1/sources.py b/letta/server/rest_api/routers/v1/sources.py index efba90c3..992eb064 100644 --- a/letta/server/rest_api/routers/v1/sources.py +++ b/letta/server/rest_api/routers/v1/sources.py @@ -2,6 +2,7 @@ import asyncio import mimetypes import os import tempfile +from datetime import datetime, timedelta, timezone from pathlib import Path from typing import List, Optional @@ -393,6 +394,31 @@ async def get_file_metadata( if file_metadata.source_id != source_id: raise HTTPException(status_code=404, detail=f"File with id={file_id} not found in source {source_id}.") + # Check for timeout if status is not terminal + if not file_metadata.processing_status.is_terminal_state(): + if file_metadata.created_at: + # Handle timezone differences between PostgreSQL (timezone-aware) and SQLite (timezone-naive) + if settings.letta_pg_uri_no_default: + # PostgreSQL: both datetimes are timezone-aware + timeout_threshold = datetime.now(timezone.utc) - timedelta(minutes=settings.file_processing_timeout_minutes) + file_created_at = file_metadata.created_at + else: + # SQLite: both datetimes should be timezone-naive + timeout_threshold = datetime.utcnow() - timedelta(minutes=settings.file_processing_timeout_minutes) + file_created_at = file_metadata.created_at + + if file_created_at < timeout_threshold: + # Move file to error status with timeout message + timeout_message = settings.file_processing_timeout_error_message.format(settings.file_processing_timeout_minutes) + try: + file_metadata = await server.file_manager.update_file_status( + file_id=file_metadata.id, actor=actor, processing_status=FileProcessingStatus.ERROR, error_message=timeout_message + ) + except ValueError as e: + # state transition was blocked - log it but don't fail the request + logger.warning(f"Could not update file to timeout error state: {str(e)}") + # continue with existing file_metadata + if should_use_pinecone() and file_metadata.processing_status == FileProcessingStatus.EMBEDDING: ids = await list_pinecone_index_for_files(file_id=file_id, actor=actor) logger.info( diff --git a/letta/settings.py b/letta/settings.py index 1d200552..f7537797 100644 --- a/letta/settings.py +++ b/letta/settings.py @@ -278,6 +278,10 @@ class Settings(BaseSettings): pinecone_agent_index: Optional[str] = "recall" upsert_pinecone_indices: bool = False + # File processing timeout settings + file_processing_timeout_minutes: int = 30 + file_processing_timeout_error_message: str = "File processing timed out after {} minutes. Please try again." + @property def letta_pg_uri(self) -> str: if self.pg_uri: diff --git a/tests/test_sources.py b/tests/test_sources.py index a3f49c82..e9315c3b 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -2,6 +2,7 @@ import os import re import threading import time +from datetime import datetime, timedelta import pytest from dotenv import load_dotenv @@ -13,6 +14,7 @@ from letta_client.types import AgentState from letta.constants import DEFAULT_ORG_ID, FILES_TOOLS from letta.orm.enums import ToolType +from letta.schemas.enums import FileProcessingStatus from letta.schemas.message import MessageCreate from letta.schemas.user import User from letta.settings import settings @@ -1045,3 +1047,66 @@ def test_agent_close_all_open_files(disable_pinecone, client: LettaSDKClient, ag # Verify result is a list of strings assert isinstance(result, list), f"Expected list, got {type(result)}" assert all(isinstance(item, str) for item in result), "All items in result should be strings" + + +def test_file_processing_timeout(disable_pinecone, client: LettaSDKClient): + """Test that files in non-terminal states are moved to error after timeout""" + # Create a source + source = client.sources.create(name="test_timeout_source", embedding="openai/text-embedding-3-small") + + # Upload a file + file_path = "tests/data/test.txt" + with open(file_path, "rb") as f: + file_metadata = client.sources.files.upload(source_id=source.id, file=f) + + # Get the file ID + file_id = file_metadata.id + + # Test the is_terminal_state method directly (this doesn't require server mocking) + assert FileProcessingStatus.COMPLETED.is_terminal_state() == True + assert FileProcessingStatus.ERROR.is_terminal_state() == True + assert FileProcessingStatus.PARSING.is_terminal_state() == False + assert FileProcessingStatus.EMBEDDING.is_terminal_state() == False + assert FileProcessingStatus.PENDING.is_terminal_state() == False + + # For testing the actual timeout logic, we can check the current file status + current_file = client.sources.get_file_metadata(source_id=source.id, file_id=file_id) + + # Convert string status to enum for testing + status_enum = FileProcessingStatus(current_file.processing_status) + + # Verify that files in terminal states are not affected by timeout checks + if status_enum.is_terminal_state(): + # This is the expected behavior - files that completed processing shouldn't timeout + print(f"File {file_id} is in terminal state: {current_file.processing_status}") + assert status_enum in [FileProcessingStatus.COMPLETED, FileProcessingStatus.ERROR] + else: + # If file is still processing, it should eventually complete or timeout + # In a real scenario, we'd wait and check, but for unit tests we just verify the logic exists + print(f"File {file_id} is still processing: {current_file.processing_status}") + assert status_enum in [FileProcessingStatus.PENDING, FileProcessingStatus.PARSING, FileProcessingStatus.EMBEDDING] + + +@pytest.mark.unit +def test_file_processing_timeout_logic(): + """Test the timeout logic directly without server dependencies""" + from datetime import timezone + + # Test scenario: file created 35 minutes ago, timeout is 30 minutes + old_time = datetime.now(timezone.utc) - timedelta(minutes=35) + current_time = datetime.now(timezone.utc) + timeout_minutes = 30 + + # Calculate timeout threshold + timeout_threshold = current_time - timedelta(minutes=timeout_minutes) + + # Verify timeout logic + assert old_time < timeout_threshold, "File created 35 minutes ago should be past 30-minute timeout" + + # Test edge case: file created exactly at timeout + edge_time = current_time - timedelta(minutes=timeout_minutes) + assert not (edge_time < timeout_threshold), "File created exactly at timeout should not trigger timeout" + + # Test recent file + recent_time = current_time - timedelta(minutes=10) + assert not (recent_time < timeout_threshold), "Recent file should not trigger timeout" From be7f4be4d8ab79f5c0ff31f87d1ee2fa9f843e88 Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 24 Jul 2025 13:48:08 -0700 Subject: [PATCH 05/24] fix: Fix state transitions for file processing (#3541) --- letta/schemas/providers/openai.py | 2 +- letta/server/rest_api/routers/v1/sources.py | 2 + letta/services/file_manager.py | 12 ++- .../services/file_processor/file_processor.py | 15 +++- tests/test_file_processor.py | 74 +++++++++++++++++++ tests/test_managers.py | 53 +++++++++++-- 6 files changed, 142 insertions(+), 16 deletions(-) diff --git a/letta/schemas/providers/openai.py b/letta/schemas/providers/openai.py index 2a3bc0b8..e1cb633e 100644 --- a/letta/schemas/providers/openai.py +++ b/letta/schemas/providers/openai.py @@ -202,7 +202,7 @@ class OpenAIProvider(Provider): if model_type not in ["text->embedding"]: continue else: - logger.info( + logger.debug( f"Skipping embedding models for %s by default, as we don't assume embeddings are supported." "Please open an issue on GitHub if support is required.", self.base_url, diff --git a/letta/server/rest_api/routers/v1/sources.py b/letta/server/rest_api/routers/v1/sources.py index 992eb064..f224d0a7 100644 --- a/letta/server/rest_api/routers/v1/sources.py +++ b/letta/server/rest_api/routers/v1/sources.py @@ -431,9 +431,11 @@ async def get_file_metadata( else: file_status = FileProcessingStatus.COMPLETED try: + print("GETTING PINECONE!!!") file_metadata = await server.file_manager.update_file_status( file_id=file_metadata.id, actor=actor, chunks_embedded=len(ids), processing_status=file_status ) + print(file_metadata) except ValueError as e: # state transition was blocked - this is a race condition # log it but don't fail the request since we're just reading metadata diff --git a/letta/services/file_manager.py b/letta/services/file_manager.py index b86d766c..5dbbe5d7 100644 --- a/letta/services/file_manager.py +++ b/letta/services/file_manager.py @@ -205,11 +205,17 @@ class FileManager: if processing_status is not None: # enforce specific transitions based on target status if processing_status == FileProcessingStatus.PARSING: - where_conditions.append(FileMetadataModel.processing_status == FileProcessingStatus.PENDING) + where_conditions.append( + FileMetadataModel.processing_status.in_([FileProcessingStatus.PENDING, FileProcessingStatus.PARSING]) + ) elif processing_status == FileProcessingStatus.EMBEDDING: - where_conditions.append(FileMetadataModel.processing_status == FileProcessingStatus.PARSING) + where_conditions.append( + FileMetadataModel.processing_status.in_([FileProcessingStatus.PARSING, FileProcessingStatus.EMBEDDING]) + ) elif processing_status == FileProcessingStatus.COMPLETED: - where_conditions.append(FileMetadataModel.processing_status == FileProcessingStatus.EMBEDDING) + where_conditions.append( + FileMetadataModel.processing_status.in_([FileProcessingStatus.EMBEDDING, FileProcessingStatus.COMPLETED]) + ) # ERROR can be set from any non-terminal state (already handled by terminal check above) # fast in-place update with state validation diff --git a/letta/services/file_processor/file_processor.py b/letta/services/file_processor/file_processor.py index 92f6c086..2084e108 100644 --- a/letta/services/file_processor/file_processor.py +++ b/letta/services/file_processor/file_processor.py @@ -177,9 +177,7 @@ class FileProcessor: "file_processor.ocr_completed", {"filename": filename, "pages_extracted": len(ocr_response.pages), "text_length": len(raw_markdown_text)}, ) - file_metadata = await self.file_manager.update_file_status( - file_id=file_metadata.id, actor=self.actor, processing_status=FileProcessingStatus.EMBEDDING - ) + file_metadata = await self.file_manager.upsert_file_content(file_id=file_metadata.id, text=raw_markdown_text, actor=self.actor) await self.agent_manager.insert_file_into_context_windows( @@ -207,6 +205,11 @@ class FileProcessor: ) # Chunk and embed with fallback logic + if not self.using_pinecone: + file_metadata = await self.file_manager.update_file_status( + file_id=file_metadata.id, actor=self.actor, processing_status=FileProcessingStatus.EMBEDDING + ) + all_passages = await self._chunk_and_embed_with_fallback( file_metadata=file_metadata, ocr_response=ocr_response, @@ -243,12 +246,16 @@ class FileProcessor: processing_status=FileProcessingStatus.COMPLETED, ) else: - await self.file_manager.update_file_status( + print("UPDATING HERE!!!!") + + file_metadata = await self.file_manager.update_file_status( file_id=file_metadata.id, actor=self.actor, total_chunks=len(all_passages), chunks_embedded=0, + processing_status=FileProcessingStatus.EMBEDDING, ) + print(file_metadata) return all_passages diff --git a/tests/test_file_processor.py b/tests/test_file_processor.py index e2448e2e..15ba5010 100644 --- a/tests/test_file_processor.py +++ b/tests/test_file_processor.py @@ -217,3 +217,77 @@ class TestOpenAIEmbedder: assert passages[2].embedding[:2] == [0.3, 0.3] assert passages[3].text == "chunk 4" assert passages[3].embedding[:2] == [0.4, 0.4] + + +class TestFileProcessorWithPinecone: + """Test suite for file processor with Pinecone integration""" + + @pytest.mark.asyncio + async def test_file_processor_sets_chunks_embedded_zero_with_pinecone(self): + """Test that file processor sets total_chunks and chunks_embedded=0 when using Pinecone""" + from letta.schemas.enums import FileProcessingStatus + from letta.schemas.file import FileMetadata + from letta.services.file_processor.embedder.pinecone_embedder import PineconeEmbedder + from letta.services.file_processor.file_processor import FileProcessor + from letta.services.file_processor.parser.markitdown_parser import MarkitdownFileParser + + # Mock dependencies + mock_actor = Mock() + mock_actor.organization_id = "test_org" + + # Create real parser + file_parser = MarkitdownFileParser() + + # Create file metadata with content + mock_file = FileMetadata( + file_name="test.txt", + source_id="source-87654321", + processing_status=FileProcessingStatus.PARSING, + total_chunks=0, + chunks_embedded=0, + content="This is test content that will be chunked.", + ) + + # Mock only the Pinecone-specific functionality + with patch("letta.services.file_processor.embedder.pinecone_embedder.PINECONE_AVAILABLE", True): + with patch("letta.services.file_processor.embedder.pinecone_embedder.upsert_file_records_to_pinecone_index") as mock_upsert: + # Mock successful Pinecone upsert + mock_upsert.return_value = None + + # Create real Pinecone embedder + embedder = PineconeEmbedder() + + # Create file processor with Pinecone enabled + file_processor = FileProcessor(file_parser=file_parser, embedder=embedder, actor=mock_actor, using_pinecone=True) + + # Track file manager update calls + update_calls = [] + + async def track_update(*args, **kwargs): + update_calls.append(kwargs) + return mock_file + + # Mock managers to track calls + with patch.object(file_processor.file_manager, "update_file_status", new=track_update): + with patch.object(file_processor.passage_manager, "create_many_source_passages_async", new=AsyncMock()): + # Process the imported file (which has content) + await file_processor.process_imported_file(mock_file, mock_file.source_id) + + # Find the call that sets total_chunks and chunks_embedded + chunk_update_call = None + for call in update_calls: + if "total_chunks" in call and "chunks_embedded" in call: + chunk_update_call = call + break + + # Verify the correct values were set + assert chunk_update_call is not None, "No update_file_status call found with total_chunks and chunks_embedded" + assert chunk_update_call["total_chunks"] > 0, "total_chunks should be greater than 0" + assert chunk_update_call["chunks_embedded"] == 0, "chunks_embedded should be 0 when using Pinecone" + + # Verify Pinecone upsert was called + mock_upsert.assert_called_once() + call_args = mock_upsert.call_args + assert call_args.kwargs["file_id"] == mock_file.id + assert call_args.kwargs["source_id"] == mock_file.source_id + assert len(call_args.kwargs["chunks"]) > 0 diff --git a/tests/test_managers.py b/tests/test_managers.py index a6253053..2556b9d4 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -6574,14 +6574,14 @@ async def test_file_status_race_condition_prevention(server, default_user, defau processing_status=FileProcessingStatus.PARSING, ) - # Simulate race condition: Try to update from PENDING again (stale read) - # This should fail because the file is already in PARSING - with pytest.raises(ValueError, match="Invalid state transition.*parsing.*PARSING"): - await server.file_manager.update_file_status( - file_id=created.id, - actor=default_user, - processing_status=FileProcessingStatus.PARSING, - ) + # Simulate race condition: Try to update from PARSING to PARSING again (stale read) + # This should now be allowed (same-state transition) to prevent race conditions + updated_again = await server.file_manager.update_file_status( + file_id=created.id, + actor=default_user, + processing_status=FileProcessingStatus.PARSING, + ) + assert updated_again.processing_status == FileProcessingStatus.PARSING # Move to ERROR await server.file_manager.update_file_status( @@ -6685,6 +6685,43 @@ async def test_file_status_update_with_chunks_progress(server, default_user, def assert updated.chunks_embedded == 100 # preserved +@pytest.mark.asyncio +async def test_same_state_transitions_allowed(server, default_user, default_source): + """Test that same-state transitions are allowed to prevent race conditions.""" + # Create file + created = await server.file_manager.create_file( + FileMetadata( + file_name="same_state_test.txt", + source_id=default_source.id, + processing_status=FileProcessingStatus.PENDING, + ), + default_user, + ) + + # Test PARSING -> PARSING + await server.file_manager.update_file_status(file_id=created.id, actor=default_user, processing_status=FileProcessingStatus.PARSING) + updated = await server.file_manager.update_file_status( + file_id=created.id, actor=default_user, processing_status=FileProcessingStatus.PARSING + ) + assert updated.processing_status == FileProcessingStatus.PARSING + + # Test EMBEDDING -> EMBEDDING + await server.file_manager.update_file_status(file_id=created.id, actor=default_user, processing_status=FileProcessingStatus.EMBEDDING) + updated = await server.file_manager.update_file_status( + file_id=created.id, actor=default_user, processing_status=FileProcessingStatus.EMBEDDING, chunks_embedded=5 + ) + assert updated.processing_status == FileProcessingStatus.EMBEDDING + assert updated.chunks_embedded == 5 + + # Test COMPLETED -> COMPLETED + await server.file_manager.update_file_status(file_id=created.id, actor=default_user, processing_status=FileProcessingStatus.COMPLETED) + updated = await server.file_manager.update_file_status( + file_id=created.id, actor=default_user, processing_status=FileProcessingStatus.COMPLETED, total_chunks=10 + ) + assert updated.processing_status == FileProcessingStatus.COMPLETED + assert updated.total_chunks == 10 + + @pytest.mark.asyncio async def test_upsert_file_content_basic(server: SyncServer, default_user, default_source, async_session): """Test creating and updating file content with upsert_file_content().""" From 77140dcecc48fbd0519b39faa52caa6e0d550e16 Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 24 Jul 2025 14:15:23 -0700 Subject: [PATCH 06/24] feat: Lower file config defaults for larger models (#3543) --- letta/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letta/utils.py b/letta/utils.py index 2b8a4501..b2f35642 100644 --- a/letta/utils.py +++ b/letta/utils.py @@ -1236,6 +1236,6 @@ def calculate_file_defaults_based_on_context_window(context_window: Optional[int elif context_window <= 128_000: # Large models (100K-128K) return 10, 25_000 # ~62.5K tokens elif context_window <= 200_000: # Very large models (128K-200K) - return 10, 50_000 # ~128k tokens + return 10, 40_000 # ~100k tokens else: # Extremely large models (200K+) - return 15, 50_000 # ~187.5k tokens + return 15, 40_000 # ~1505k tokens From 357e30fc559e78e9e0dfbcf1a55e5c95651bc8c2 Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 24 Jul 2025 14:29:42 -0700 Subject: [PATCH 07/24] fix: Adjust immediate complete for pinecone file state machine (#3546) --- letta/server/rest_api/routers/v1/sources.py | 2 -- .../services/file_processor/file_processor.py | 27 ++++++++----------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/letta/server/rest_api/routers/v1/sources.py b/letta/server/rest_api/routers/v1/sources.py index f224d0a7..992eb064 100644 --- a/letta/server/rest_api/routers/v1/sources.py +++ b/letta/server/rest_api/routers/v1/sources.py @@ -431,11 +431,9 @@ async def get_file_metadata( else: file_status = FileProcessingStatus.COMPLETED try: - print("GETTING PINECONE!!!") file_metadata = await server.file_manager.update_file_status( file_id=file_metadata.id, actor=actor, chunks_embedded=len(ids), processing_status=file_status ) - print(file_metadata) except ValueError as e: # state transition was blocked - this is a race condition # log it but don't fail the request since we're just reading metadata diff --git a/letta/services/file_processor/file_processor.py b/letta/services/file_processor/file_processor.py index 2084e108..d27597a4 100644 --- a/letta/services/file_processor/file_processor.py +++ b/letta/services/file_processor/file_processor.py @@ -69,6 +69,15 @@ class FileProcessor: raise ValueError("No chunks created from text") all_chunks.extend(chunks) + # Update with chunks length + file_metadata = await self.file_manager.update_file_status( + file_id=file_metadata.id, + actor=self.actor, + processing_status=FileProcessingStatus.EMBEDDING, + total_chunks=len(all_chunks), + chunks_embedded=0, + ) + all_passages = await self.embedder.generate_embedded_passages( file_id=file_metadata.id, source_id=source_id, @@ -205,11 +214,6 @@ class FileProcessor: ) # Chunk and embed with fallback logic - if not self.using_pinecone: - file_metadata = await self.file_manager.update_file_status( - file_id=file_metadata.id, actor=self.actor, processing_status=FileProcessingStatus.EMBEDDING - ) - all_passages = await self._chunk_and_embed_with_fallback( file_metadata=file_metadata, ocr_response=ocr_response, @@ -244,18 +248,8 @@ class FileProcessor: file_id=file_metadata.id, actor=self.actor, processing_status=FileProcessingStatus.COMPLETED, + chunks_embedded=len(all_passages), ) - else: - print("UPDATING HERE!!!!") - - file_metadata = await self.file_manager.update_file_status( - file_id=file_metadata.id, - actor=self.actor, - total_chunks=len(all_passages), - chunks_embedded=0, - processing_status=FileProcessingStatus.EMBEDDING, - ) - print(file_metadata) return all_passages @@ -293,6 +287,7 @@ class FileProcessor: document_annotation=None, ) + # TODO: The file state machine here is kind of out of date, we need to match with the correct one above @trace_method async def process_imported_file(self, file_metadata: FileMetadata, source_id: str) -> List[Passage]: """Process an imported file that already has content - skip OCR, do chunking/embedding""" From 94d10589c8e3ac6eb3713970c67eba03e3951b64 Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:50:52 -0700 Subject: [PATCH 08/24] fix: structured outputs for send_message, LettaMessage --- letta/helpers/datetime_helpers.py | 2 +- letta/helpers/json_helpers.py | 2 +- letta/schemas/message.py | 189 ++++++++++++++++-------------- letta/system.py | 2 +- letta/utils.py | 50 +++----- tests/test_utils.py | 123 ++++++++++++++++++- 6 files changed, 238 insertions(+), 130 deletions(-) diff --git a/letta/helpers/datetime_helpers.py b/letta/helpers/datetime_helpers.py index 0633add3..07856495 100644 --- a/letta/helpers/datetime_helpers.py +++ b/letta/helpers/datetime_helpers.py @@ -42,7 +42,7 @@ def get_local_time_timezone(timezone=DEFAULT_TIMEZONE): return formatted_time -def get_local_time(timezone=DEFAULT_TIMEZONE): +def get_local_time(timezone: str | None = DEFAULT_TIMEZONE): if timezone is not None: time_str = get_local_time_timezone(timezone) else: diff --git a/letta/helpers/json_helpers.py b/letta/helpers/json_helpers.py index 2075c58d..6618f274 100644 --- a/letta/helpers/json_helpers.py +++ b/letta/helpers/json_helpers.py @@ -7,7 +7,7 @@ def json_loads(data): return json.loads(data, strict=False) -def json_dumps(data, indent=2): +def json_dumps(data, indent=2) -> str: def safe_serializer(obj): if isinstance(obj, datetime): return obj.isoformat() diff --git a/letta/schemas/message.py b/letta/schemas/message.py index 73bf73db..8c15bead 100644 --- a/letta/schemas/message.py +++ b/letta/schemas/message.py @@ -41,7 +41,7 @@ from letta.schemas.letta_message_content import ( get_letta_message_content_union_str_json_schema, ) from letta.system import unpack_message -from letta.utils import parse_json +from letta.utils import parse_json, validate_function_response def add_inner_thoughts_to_tool_call( @@ -251,10 +251,10 @@ class Message(BaseMessage): include_err: Optional[bool] = None, ) -> List[LettaMessage]: """Convert message object (in DB format) to the style used by the original Letta API""" - messages = [] + # TODO (cliandy): break this into more manageable pieces if self.role == MessageRole.assistant: - + messages = [] # Handle reasoning if self.content: # Check for ReACT-style COT inside of TextContent @@ -348,7 +348,7 @@ class Message(BaseMessage): # We need to unpack the actual message contents from the function call try: func_args = parse_json(tool_call.function.arguments) - message_string = func_args[assistant_message_tool_kwarg] + message_string = validate_function_response(func_args[assistant_message_tool_kwarg], 0, truncate=False) except KeyError: raise ValueError(f"Function call {tool_call.function.name} missing {assistant_message_tool_kwarg} argument") messages.append( @@ -380,99 +380,106 @@ class Message(BaseMessage): is_err=self.is_err, ) ) + elif self.role == MessageRole.tool: - # This is type ToolReturnMessage - # Try to interpret the function return, recall that this is how we packaged: - # def package_function_response(was_success, response_string, timestamp=None): - # formatted_time = get_local_time() if timestamp is None else timestamp - # packaged_message = { - # "status": "OK" if was_success else "Failed", - # "message": response_string, - # "time": formatted_time, - # } - if self.content and len(self.content) == 1 and isinstance(self.content[0], TextContent): - text_content = self.content[0].text - else: - raise ValueError(f"Invalid tool return (no text object on message): {self.content}") - - try: - function_return = parse_json(text_content) - text_content = str(function_return.get("message", text_content)) - status = function_return["status"] - if status == "OK": - status_enum = "success" - elif status == "Failed": - status_enum = "error" - else: - raise ValueError(f"Invalid status: {status}") - except json.JSONDecodeError: - raise ValueError(f"Failed to decode function return: {text_content}") - assert self.tool_call_id is not None - messages.append( - # TODO make sure this is what the API returns - # function_return may not match exactly... - ToolReturnMessage( - id=self.id, - date=self.created_at, - tool_return=text_content, - status=self.tool_returns[0].status if self.tool_returns else status_enum, - tool_call_id=self.tool_call_id, - stdout=self.tool_returns[0].stdout if self.tool_returns else None, - stderr=self.tool_returns[0].stderr if self.tool_returns else None, - name=self.name, - otid=Message.generate_otid_from_id(self.id, len(messages)), - sender_id=self.sender_id, - step_id=self.step_id, - is_err=self.is_err, - ) - ) + messages = [self._convert_tool_message()] elif self.role == MessageRole.user: - # This is type UserMessage - if self.content and len(self.content) == 1 and isinstance(self.content[0], TextContent): - text_content = self.content[0].text - elif self.content: - text_content = self.content - else: - raise ValueError(f"Invalid user message (no text object on message): {self.content}") - - message = unpack_message(text_content) - messages.append( - UserMessage( - id=self.id, - date=self.created_at, - content=message, - name=self.name, - otid=self.otid, - sender_id=self.sender_id, - step_id=self.step_id, - is_err=self.is_err, - ) - ) + messages = [self._convert_user_message()] elif self.role == MessageRole.system: - # This is type SystemMessage - if self.content and len(self.content) == 1 and isinstance(self.content[0], TextContent): - text_content = self.content[0].text - else: - raise ValueError(f"Invalid system message (no text object on system): {self.content}") - - messages.append( - SystemMessage( - id=self.id, - date=self.created_at, - content=text_content, - name=self.name, - otid=self.otid, - sender_id=self.sender_id, - step_id=self.step_id, - ) - ) + messages = [self._convert_system_message()] else: - raise ValueError(self.role) + raise ValueError(f"Unknown role: {self.role}") - if reverse: - messages.reverse() + return messages[::-1] if reverse else messages - return messages + def _convert_tool_message(self) -> ToolReturnMessage: + """Convert tool role message to ToolReturnMessage + + the tool return is packaged as follows: + packaged_message = { + "status": "OK" if was_success else "Failed", + "message": response_string, + "time": formatted_time, + } + """ + if self.content and len(self.content) == 1 and isinstance(self.content[0], TextContent): + text_content = self.content[0].text + else: + raise ValueError(f"Invalid tool return (no text object on message): {self.content}") + + try: + function_return = parse_json(text_content) + message_text = str(function_return.get("message", text_content)) + status = self._parse_tool_status(function_return["status"]) + except json.JSONDecodeError: + raise ValueError(f"Failed to decode function return: {text_content}") + + assert self.tool_call_id is not None + + return ToolReturnMessage( + id=self.id, + date=self.created_at, + tool_return=message_text, + status=self.tool_returns[0].status if self.tool_returns else status, + tool_call_id=self.tool_call_id, + stdout=self.tool_returns[0].stdout if self.tool_returns else None, + stderr=self.tool_returns[0].stderr if self.tool_returns else None, + name=self.name, + otid=Message.generate_otid_from_id(self.id, 0), + sender_id=self.sender_id, + step_id=self.step_id, + is_err=self.is_err, + ) + + @staticmethod + def _parse_tool_status(status: str) -> Literal["success", "error"]: + """Convert tool status string to enum value""" + if status == "OK": + return "success" + elif status == "Failed": + return "error" + else: + raise ValueError(f"Invalid status: {status}") + + def _convert_user_message(self) -> UserMessage: + """Convert user role message to UserMessage""" + # Extract text content + if self.content and len(self.content) == 1 and isinstance(self.content[0], TextContent): + text_content = self.content[0].text + elif self.content: + text_content = self.content + else: + raise ValueError(f"Invalid user message (no text object on message): {self.content}") + + message = unpack_message(text_content) + + return UserMessage( + id=self.id, + date=self.created_at, + content=message, + name=self.name, + otid=self.otid, + sender_id=self.sender_id, + step_id=self.step_id, + is_err=self.is_err, + ) + + def _convert_system_message(self) -> SystemMessage: + """Convert system role message to SystemMessage""" + if self.content and len(self.content) == 1 and isinstance(self.content[0], TextContent): + text_content = self.content[0].text + else: + raise ValueError(f"Invalid system message (no text object on system): {self.content}") + + return SystemMessage( + id=self.id, + date=self.created_at, + content=text_content, + name=self.name, + otid=self.otid, + sender_id=self.sender_id, + step_id=self.step_id, + ) @staticmethod def dict_to_message( diff --git a/letta/system.py b/letta/system.py index 888cc304..f64cc9d0 100644 --- a/letta/system.py +++ b/letta/system.py @@ -141,7 +141,7 @@ def package_user_message( return json_dumps(packaged_message) -def package_function_response(was_success, response_string, timezone): +def package_function_response(was_success: bool, response_string: str, timezone: str | None) -> str: formatted_time = get_local_time(timezone=timezone) packaged_message = { "status": "OK" if was_success else "Failed", diff --git a/letta/utils.py b/letta/utils.py index b2f35642..73abb1a3 100644 --- a/letta/utils.py +++ b/letta/utils.py @@ -27,7 +27,6 @@ from sqlalchemy import text import letta from letta.constants import ( - CLI_WARNING_PREFIX, CORE_MEMORY_HUMAN_CHAR_LIMIT, CORE_MEMORY_PERSONA_CHAR_LIMIT, DEFAULT_CORE_MEMORY_SOURCE_CHAR_LIMIT, @@ -851,47 +850,32 @@ def parse_json(string) -> dict: raise e -def validate_function_response(function_response_string: any, return_char_limit: int, strict: bool = False, truncate: bool = True) -> str: +def validate_function_response(function_response: Any, return_char_limit: int, strict: bool = False, truncate: bool = True) -> str: """Check to make sure that a function used by Letta returned a valid response. Truncates to return_char_limit if necessary. - Responses need to be strings (or None) that fall under a certain text count limit. + This makes sure that we can coerce the function_response into a string that meets our criteria. We handle some soft coercion. + If strict is True, we raise a ValueError if function_response is not a string or None. """ - if not isinstance(function_response_string, str): - # Soft correction for a few basic types + if isinstance(function_response, str): + function_response_string = function_response - if function_response_string is None: - # function_response_string = "Empty (no function output)" - function_response_string = "None" # backcompat + elif function_response is None: + function_response_string = "None" - elif isinstance(function_response_string, dict): - if strict: - # TODO add better error message - raise ValueError(function_response_string) + elif strict: + raise ValueError(f"Strict mode violation. Function returned type: {type(function_response).__name__}") - # Allow dict through since it will be cast to json.dumps() - try: - # TODO find a better way to do this that won't result in double escapes - function_response_string = json_dumps(function_response_string) - except: - raise ValueError(function_response_string) + elif isinstance(function_response, dict): + # As functions can return arbitrary data, if there's already nesting somewhere in the response, it's difficult + # for us to not result in double escapes. + function_response_string = json_dumps(function_response) + else: + logger.debug(f"Function returned type {type(function_response).__name__}. Coercing to string.") + function_response_string = str(function_response) - else: - if strict: - # TODO add better error message - raise ValueError(function_response_string) - - # Try to convert to a string, but throw a warning to alert the user - try: - function_response_string = str(function_response_string) - except: - raise ValueError(function_response_string) - - # Now check the length and make sure it doesn't go over the limit # TODO we should change this to a max token limit that's variable based on tokens remaining (or context-window) if truncate and len(function_response_string) > return_char_limit: - print( - f"{CLI_WARNING_PREFIX}function return was over limit ({len(function_response_string)} > {return_char_limit}) and was truncated" - ) + logger.warning(f"function return was over limit ({len(function_response_string)} > {return_char_limit}) and was truncated") function_response_string = f"{function_response_string[:return_char_limit]}... [NOTE: function output was truncated since it exceeded the character limit ({len(function_response_string)} > {return_char_limit})]" return function_response_string diff --git a/tests/test_utils.py b/tests/test_utils.py index 23658b84..84622882 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,3 @@ -import asyncio - import pytest from letta.constants import MAX_FILENAME_LENGTH @@ -7,7 +5,7 @@ from letta.functions.ast_parsers import coerce_dict_args_by_annotations, get_fun from letta.schemas.file import FileMetadata from letta.services.file_processor.chunker.line_chunker import LineChunker from letta.services.helpers.agent_manager_helper import safe_format -from letta.utils import sanitize_filename +from letta.utils import sanitize_filename, validate_function_response CORE_MEMORY_VAR = "My core memory is that I like to eat bananas" VARS_DICT = {"CORE_MEMORY": CORE_MEMORY_VAR} @@ -555,3 +553,122 @@ async def test_get_latest_alembic_revision_consistency(event_loop): # They should be identical assert revision_id1 == revision_id2 + + +# ---------------------- validate_function_response TESTS ---------------------- # + + +def test_validate_function_response_string_input(): + """Test that string inputs are returned unchanged when within limit""" + response = validate_function_response("hello world", return_char_limit=100) + assert response == "hello world" + + +def test_validate_function_response_none_input(): + """Test that None inputs are converted to 'None' string""" + response = validate_function_response(None, return_char_limit=100) + assert response == "None" + + +def test_validate_function_response_dict_input(): + """Test that dict inputs are JSON serialized""" + test_dict = {"key": "value", "number": 42} + response = validate_function_response(test_dict, return_char_limit=100) + # Response should be valid JSON string + import json + + parsed = json.loads(response) + assert parsed == test_dict + + +def test_validate_function_response_other_types(): + """Test that other types are converted to strings""" + # Test integer + response = validate_function_response(42, return_char_limit=100) + assert response == "42" + + # Test list + response = validate_function_response([1, 2, 3], return_char_limit=100) + assert response == "[1, 2, 3]" + + # Test boolean + response = validate_function_response(True, return_char_limit=100) + assert response == "True" + + +def test_validate_function_response_strict_mode_string(): + """Test strict mode allows strings""" + response = validate_function_response("test", return_char_limit=100, strict=True) + assert response == "test" + + +def test_validate_function_response_strict_mode_none(): + """Test strict mode allows None""" + response = validate_function_response(None, return_char_limit=100, strict=True) + assert response == "None" + + +def test_validate_function_response_strict_mode_violation(): + """Test strict mode raises ValueError for non-string/None types""" + with pytest.raises(ValueError, match="Strict mode violation. Function returned type: int"): + validate_function_response(42, return_char_limit=100, strict=True) + + with pytest.raises(ValueError, match="Strict mode violation. Function returned type: dict"): + validate_function_response({"key": "value"}, return_char_limit=100, strict=True) + + +def test_validate_function_response_truncation(): + """Test that long responses are truncated when truncate=True""" + long_string = "a" * 200 + response = validate_function_response(long_string, return_char_limit=50, truncate=True) + assert len(response) > 50 # Should include truncation message + assert response.startswith("a" * 50) + assert "NOTE: function output was truncated" in response + assert "200 > 50" in response + + +def test_validate_function_response_no_truncation(): + """Test that long responses are not truncated when truncate=False""" + long_string = "a" * 200 + response = validate_function_response(long_string, return_char_limit=50, truncate=False) + assert response == long_string + assert len(response) == 200 + + +def test_validate_function_response_exact_limit(): + """Test response exactly at the character limit""" + exact_string = "a" * 50 + response = validate_function_response(exact_string, return_char_limit=50, truncate=True) + assert response == exact_string + + +def test_validate_function_response_complex_dict(): + """Test with complex nested dictionary""" + complex_dict = {"nested": {"key": "value"}, "list": [1, 2, {"inner": "dict"}], "null": None, "bool": True} + response = validate_function_response(complex_dict, return_char_limit=1000) + # Should be valid JSON + import json + + parsed = json.loads(response) + assert parsed == complex_dict + + +def test_validate_function_response_dict_truncation(): + """Test that serialized dict gets truncated properly""" + # Create a dict that when serialized will exceed limit + large_dict = {"data": "x" * 100} + response = validate_function_response(large_dict, return_char_limit=20, truncate=True) + assert "NOTE: function output was truncated" in response + assert len(response) > 20 # Includes truncation message + + +def test_validate_function_response_empty_string(): + """Test empty string handling""" + response = validate_function_response("", return_char_limit=100) + assert response == "" + + +def test_validate_function_response_whitespace(): + """Test whitespace-only string handling""" + response = validate_function_response(" \n\t ", return_char_limit=100) + assert response == " \n\t " From 66047d54f779880eabcc375c25ba1e802b23b33c Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 24 Jul 2025 15:14:00 -0700 Subject: [PATCH 09/24] fix: Fix test managers state transition tests (#3551) --- letta/services/file_manager.py | 42 +++++++++++++++++++--------------- tests/test_managers.py | 1 + 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/letta/services/file_manager.py b/letta/services/file_manager.py index 5dbbe5d7..6dc06778 100644 --- a/letta/services/file_manager.py +++ b/letta/services/file_manager.py @@ -151,7 +151,8 @@ class FileManager: Enforces state transition rules (when enforce_state_transitions=True): - PENDING -> PARSING -> EMBEDDING -> COMPLETED (normal flow) - Any non-terminal state -> ERROR - - ERROR and COMPLETED are terminal (no transitions allowed) + - Same-state transitions are allowed (e.g., EMBEDDING -> EMBEDDING) + - ERROR and COMPLETED are terminal (no status transitions allowed, metadata updates blocked) Args: file_id: ID of the file to update @@ -196,28 +197,31 @@ class FileManager: ] # only add state transition validation if enforce_state_transitions is True - if enforce_state_transitions: - # prevent updates to terminal states (ERROR, COMPLETED) + if enforce_state_transitions and processing_status is not None: + # enforce specific transitions based on target status + if processing_status == FileProcessingStatus.PARSING: + where_conditions.append( + FileMetadataModel.processing_status.in_([FileProcessingStatus.PENDING, FileProcessingStatus.PARSING]) + ) + elif processing_status == FileProcessingStatus.EMBEDDING: + where_conditions.append( + FileMetadataModel.processing_status.in_([FileProcessingStatus.PARSING, FileProcessingStatus.EMBEDDING]) + ) + elif processing_status == FileProcessingStatus.COMPLETED: + where_conditions.append( + FileMetadataModel.processing_status.in_([FileProcessingStatus.EMBEDDING, FileProcessingStatus.COMPLETED]) + ) + elif processing_status == FileProcessingStatus.ERROR: + # ERROR can be set from any non-terminal state + where_conditions.append( + FileMetadataModel.processing_status.notin_([FileProcessingStatus.ERROR, FileProcessingStatus.COMPLETED]) + ) + elif enforce_state_transitions and processing_status is None: + # If only updating metadata fields (not status), prevent updates to terminal states where_conditions.append( FileMetadataModel.processing_status.notin_([FileProcessingStatus.ERROR, FileProcessingStatus.COMPLETED]) ) - if processing_status is not None: - # enforce specific transitions based on target status - if processing_status == FileProcessingStatus.PARSING: - where_conditions.append( - FileMetadataModel.processing_status.in_([FileProcessingStatus.PENDING, FileProcessingStatus.PARSING]) - ) - elif processing_status == FileProcessingStatus.EMBEDDING: - where_conditions.append( - FileMetadataModel.processing_status.in_([FileProcessingStatus.PARSING, FileProcessingStatus.EMBEDDING]) - ) - elif processing_status == FileProcessingStatus.COMPLETED: - where_conditions.append( - FileMetadataModel.processing_status.in_([FileProcessingStatus.EMBEDDING, FileProcessingStatus.COMPLETED]) - ) - # ERROR can be set from any non-terminal state (already handled by terminal check above) - # fast in-place update with state validation stmt = ( update(FileMetadataModel) diff --git a/tests/test_managers.py b/tests/test_managers.py index 2556b9d4..c75b6167 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -55,6 +55,7 @@ from letta.schemas.block import BlockUpdate, CreateBlock from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import ActorType, AgentStepStatus, FileProcessingStatus, JobStatus, JobType, MessageRole, ProviderType from letta.schemas.environment_variables import SandboxEnvironmentVariableCreate, SandboxEnvironmentVariableUpdate +from letta.schemas.file import FileMetadata from letta.schemas.file import FileMetadata as PydanticFileMetadata from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityPropertyType, IdentityType, IdentityUpdate, IdentityUpsert from letta.schemas.job import BatchJob From 7781ca55bcdbf23373e1cd29612bbb07121748a1 Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:51:31 -0700 Subject: [PATCH 10/24] fix: error logging for stop reasons --- letta/agents/letta_agent.py | 6 ++++++ tests/test_utils.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/letta/agents/letta_agent.py b/letta/agents/letta_agent.py index 3c7960bc..3f7faab7 100644 --- a/letta/agents/letta_agent.py +++ b/letta/agents/letta_agent.py @@ -359,6 +359,8 @@ class LettaAgent(BaseAgent): # Update step if it needs to be updated finally: if settings.track_stop_reason: + if step_progression == StepProgression.FINISHED and should_continue: + continue self.logger.info("Running final update. Step Progression: %s", step_progression) try: if step_progression < StepProgression.STEP_LOGGED: @@ -586,6 +588,8 @@ class LettaAgent(BaseAgent): # Update step if it needs to be updated finally: if settings.track_stop_reason: + if step_progression == StepProgression.FINISHED and should_continue: + continue self.logger.info("Running final update. Step Progression: %s", step_progression) try: if step_progression < StepProgression.STEP_LOGGED: @@ -897,6 +901,8 @@ class LettaAgent(BaseAgent): # Update step if it needs to be updated finally: if settings.track_stop_reason: + if step_progression == StepProgression.FINISHED and should_continue: + continue self.logger.info("Running final update. Step Progression: %s", step_progression) try: if step_progression < StepProgression.STEP_LOGGED: diff --git a/tests/test_utils.py b/tests/test_utils.py index 84622882..87cfc127 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,5 @@ +import asyncio + import pytest from letta.constants import MAX_FILENAME_LENGTH From bc471c6055d97a904748c9e88e5f427771d87410 Mon Sep 17 00:00:00 2001 From: jnjpng Date: Thu, 24 Jul 2025 18:17:08 -0700 Subject: [PATCH 11/24] feat: support mcp server export/import with agent files Co-authored-by: Jin Peng --- letta/schemas/agent_file.py | 36 +- letta/server/rest_api/routers/v1/tools.py | 6 +- letta/services/agent_serialization_manager.py | 91 ++++- letta/services/mcp_manager.py | 39 +- letta/services/tool_manager.py | 12 +- tests/mcp/mcp_config.json | 2 +- tests/test_agent_serialization_v2.py | 339 +++++++++++++++++- tests/test_managers.py | 83 ++++- 8 files changed, 573 insertions(+), 35 deletions(-) diff --git a/letta/schemas/agent_file.py b/letta/schemas/agent_file.py index 26b45cb0..c0e4fa47 100644 --- a/letta/schemas/agent_file.py +++ b/letta/schemas/agent_file.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from pydantic import BaseModel, Field @@ -8,6 +8,7 @@ from letta.schemas.block import Block, CreateBlock from letta.schemas.enums import MessageRole from letta.schemas.file import FileAgent, FileAgentBase, FileMetadata, FileMetadataBase from letta.schemas.group import GroupCreate +from letta.schemas.mcp import MCPServer from letta.schemas.message import Message, MessageCreate from letta.schemas.source import Source, SourceCreate from letta.schemas.tool import Tool @@ -264,9 +265,34 @@ class ToolSchema(Tool): return cls(**tool.model_dump()) -# class MCPServerSchema(RegisterMCPServer): -# """MCP Server with human-readable ID for agent file""" -# id: str = Field(..., description="Human-readable identifier for this MCP server in the file") +class MCPServerSchema(BaseModel): + """MCP server schema for agent files with remapped ID.""" + + __id_prefix__ = "mcp_server" + + id: str = Field(..., description="Human-readable MCP server ID") + server_type: str + server_name: str + server_url: Optional[str] = None + stdio_config: Optional[Dict[str, Any]] = None + metadata_: Optional[Dict[str, Any]] = None + + @classmethod + def from_mcp_server(cls, mcp_server: MCPServer) -> "MCPServerSchema": + """Convert MCPServer to MCPServerSchema (excluding auth fields).""" + return cls( + id=mcp_server.id, # remapped by serialization manager + server_type=mcp_server.server_type, + server_name=mcp_server.server_name, + server_url=mcp_server.server_url, + # exclude token, custom_headers, and the env field in stdio_config that may contain authentication credentials + stdio_config=cls.strip_env_from_stdio_config(mcp_server.stdio_config.model_dump()) if mcp_server.stdio_config else None, + metadata_=mcp_server.metadata_, + ) + + def strip_env_from_stdio_config(stdio_config: Dict[str, Any]) -> Dict[str, Any]: + """Strip out the env field from the stdio config.""" + return {k: v for k, v in stdio_config.items() if k != "env"} class AgentFileSchema(BaseModel): @@ -278,7 +304,7 @@ class AgentFileSchema(BaseModel): files: List[FileSchema] = Field(..., description="List of files in this agent file") sources: List[SourceSchema] = Field(..., description="List of sources in this agent file") tools: List[ToolSchema] = Field(..., description="List of tools in this agent file") - # mcp_servers: List[MCPServerSchema] = Field(..., description="List of MCP servers in this agent file") + mcp_servers: List[MCPServerSchema] = Field(..., description="List of MCP servers in this agent file") metadata: Dict[str, str] = Field( default_factory=dict, description="Metadata for this agent file, including revision_id and other export information." ) diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index 7e840a8c..ad171613 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -475,7 +475,11 @@ async def add_mcp_tool( ) tool_create = ToolCreate.from_mcp(mcp_server_name=mcp_server_name, mcp_tool=mcp_tool) - return await server.tool_manager.create_mcp_tool_async(tool_create=tool_create, mcp_server_name=mcp_server_name, actor=actor) + # For config-based servers, use the server name as ID since they don't have database IDs + mcp_server_id = mcp_server_name + return await server.tool_manager.create_mcp_tool_async( + tool_create=tool_create, mcp_server_name=mcp_server_name, mcp_server_id=mcp_server_id, actor=actor + ) else: return await server.mcp_manager.add_tool_from_mcp_server(mcp_server_name=mcp_server_name, mcp_tool_name=mcp_tool_name, actor=actor) diff --git a/letta/services/agent_serialization_manager.py b/letta/services/agent_serialization_manager.py index 632fd77b..166d1608 100644 --- a/letta/services/agent_serialization_manager.py +++ b/letta/services/agent_serialization_manager.py @@ -13,6 +13,7 @@ from letta.schemas.agent_file import ( FileSchema, GroupSchema, ImportResult, + MCPServerSchema, MessageSchema, SourceSchema, ToolSchema, @@ -20,6 +21,7 @@ from letta.schemas.agent_file import ( from letta.schemas.block import Block from letta.schemas.enums import FileProcessingStatus from letta.schemas.file import FileMetadata +from letta.schemas.mcp import MCPServer from letta.schemas.message import Message from letta.schemas.source import Source from letta.schemas.tool import Tool @@ -92,7 +94,7 @@ class AgentSerializationManager: ToolSchema.__id_prefix__: 0, MessageSchema.__id_prefix__: 0, FileAgentSchema.__id_prefix__: 0, - # MCPServerSchema.__id_prefix__: 0, + MCPServerSchema.__id_prefix__: 0, } def _reset_state(self): @@ -258,6 +260,58 @@ class AgentSerializationManager: file_schema.source_id = self._map_db_to_file_id(file_metadata.source_id, SourceSchema.__id_prefix__, allow_new=False) return file_schema + async def _extract_unique_mcp_servers(self, tools: List, actor: User) -> List: + """Extract unique MCP servers from tools based on metadata, using server_id if available, otherwise falling back to server_name.""" + from letta.constants import MCP_TOOL_TAG_NAME_PREFIX + + mcp_server_ids = set() + mcp_server_names = set() + for tool in tools: + # Check if tool has MCP metadata + if tool.metadata_ and MCP_TOOL_TAG_NAME_PREFIX in tool.metadata_: + mcp_metadata = tool.metadata_[MCP_TOOL_TAG_NAME_PREFIX] + # TODO: @jnjpng clean this up once we fully migrate to server_id being the main identifier + if "server_id" in mcp_metadata: + mcp_server_ids.add(mcp_metadata["server_id"]) + elif "server_name" in mcp_metadata: + mcp_server_names.add(mcp_metadata["server_name"]) + + # Fetch MCP servers by ID + mcp_servers = [] + fetched_server_ids = set() + if mcp_server_ids: + for server_id in mcp_server_ids: + try: + mcp_server = await self.mcp_manager.get_mcp_server_by_id_async(server_id, actor) + if mcp_server: + mcp_servers.append(mcp_server) + fetched_server_ids.add(server_id) + except Exception as e: + logger.warning(f"Failed to fetch MCP server {server_id}: {e}") + + # Fetch MCP servers by name if not already fetched by ID + if mcp_server_names: + for server_name in mcp_server_names: + try: + mcp_server = await self.mcp_manager.get_mcp_server(server_name, actor) + if mcp_server and mcp_server.id not in fetched_server_ids: + mcp_servers.append(mcp_server) + except Exception as e: + logger.warning(f"Failed to fetch MCP server by name {server_name}: {e}") + + return mcp_servers + + def _convert_mcp_server_to_schema(self, mcp_server: MCPServer) -> MCPServerSchema: + """Convert MCPServer to MCPServerSchema with ID remapping and auth scrubbing""" + try: + mcp_file_id = self._map_db_to_file_id(mcp_server.id, MCPServerSchema.__id_prefix__, allow_new=False) + mcp_schema = MCPServerSchema.from_mcp_server(mcp_server) + mcp_schema.id = mcp_file_id + return mcp_schema + except Exception as e: + logger.error(f"Failed to convert MCP server {mcp_server.id}: {e}") + raise + async def export(self, agent_ids: List[str], actor: User) -> AgentFileSchema: """ Export agents and their related entities to AgentFileSchema format. @@ -289,6 +343,13 @@ class AgentSerializationManager: tool_set = self._extract_unique_tools(agent_states) block_set = self._extract_unique_blocks(agent_states) + # Extract MCP servers from tools BEFORE conversion (must be done before ID mapping) + mcp_server_set = await self._extract_unique_mcp_servers(tool_set, actor) + + # Map MCP server IDs before converting schemas + for mcp_server in mcp_server_set: + self._map_db_to_file_id(mcp_server.id, MCPServerSchema.__id_prefix__) + # Extract sources and files from agent states BEFORE conversion (with caching) source_set, file_set = await self._extract_unique_sources_and_files_from_agents(agent_states, actor, files_agents_cache) @@ -301,6 +362,7 @@ class AgentSerializationManager: block_schemas = [self._convert_block_to_schema(block) for block in block_set] source_schemas = [self._convert_source_to_schema(source) for source in source_set] file_schemas = [self._convert_file_to_schema(file_metadata) for file_metadata in file_set] + mcp_server_schemas = [self._convert_mcp_server_to_schema(mcp_server) for mcp_server in mcp_server_set] logger.info(f"Exporting {len(agent_ids)} agents to agent file format") @@ -312,7 +374,7 @@ class AgentSerializationManager: files=file_schemas, sources=source_schemas, tools=tool_schemas, - # mcp_servers=[], # TODO: Extract and convert MCP servers + mcp_servers=mcp_server_schemas, metadata={"revision_id": await get_latest_alembic_revision()}, created_at=datetime.now(timezone.utc), ) @@ -359,7 +421,20 @@ class AgentSerializationManager: # in-memory cache for file metadata to avoid repeated db calls file_metadata_cache = {} # Maps database file ID to FileMetadata - # 1. Create tools first (no dependencies) - using bulk upsert for efficiency + # 1. Create MCP servers first (tools depend on them) + if schema.mcp_servers: + for mcp_server_schema in schema.mcp_servers: + server_data = mcp_server_schema.model_dump(exclude={"id"}) + filtered_server_data = self._filter_dict_for_model(server_data, MCPServer) + create_schema = MCPServer(**filtered_server_data) + + # Note: We don't have auth info from export, so the user will need to re-configure auth. + # TODO: @jnjpng store metadata about obfuscated metadata to surface to the user + created_mcp_server = await self.mcp_manager.create_or_update_mcp_server(create_schema, actor) + file_to_db_ids[mcp_server_schema.id] = created_mcp_server.id + imported_count += 1 + + # 2. Create tools (may depend on MCP servers) - using bulk upsert for efficiency if schema.tools: # convert tool schemas to pydantic tools pydantic_tools = [] @@ -559,6 +634,7 @@ class AgentSerializationManager: (schema.files, FileSchema.__id_prefix__), (schema.sources, SourceSchema.__id_prefix__), (schema.tools, ToolSchema.__id_prefix__), + (schema.mcp_servers, MCPServerSchema.__id_prefix__), ] for entities, expected_prefix in entity_checks: @@ -601,6 +677,7 @@ class AgentSerializationManager: ("files", schema.files), ("sources", schema.sources), ("tools", schema.tools), + ("mcp_servers", schema.mcp_servers), ] for entity_type, entities in entity_collections: @@ -705,3 +782,11 @@ class AgentSerializationManager: raise AgentFileImportError(f"Schema validation failed: {'; '.join(errors)}") logger.info("Schema validation passed") + + def _filter_dict_for_model(self, data: dict, model_cls): + """Filter a dictionary to only include keys that are in the model fields""" + try: + allowed = model_cls.model_fields.keys() # Pydantic v2 + except AttributeError: + allowed = model_cls.__fields__.keys() # Pydantic v1 + return {k: v for k, v in data.items() if k in allowed} diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index b542e22b..77cd2a9a 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -98,12 +98,19 @@ class MCPManager: @enforce_types async def add_tool_from_mcp_server(self, mcp_server_name: str, mcp_tool_name: str, actor: PydanticUser) -> PydanticTool: """Add a tool from an MCP server to the Letta tool registry.""" + # get the MCP server ID, we should migrate to use the server_id instead of the name + mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor=actor) + if not mcp_server_id: + raise ValueError(f"MCP server '{mcp_server_name}' not found") + mcp_tools = await self.list_mcp_server_tools(mcp_server_name, actor=actor) for mcp_tool in mcp_tools: if mcp_tool.name == mcp_tool_name: tool_create = ToolCreate.from_mcp(mcp_server_name=mcp_server_name, mcp_tool=mcp_tool) - return await self.tool_manager.create_mcp_tool_async(tool_create=tool_create, mcp_server_name=mcp_server_name, actor=actor) + return await self.tool_manager.create_mcp_tool_async( + tool_create=tool_create, mcp_server_name=mcp_server_name, mcp_server_id=mcp_server_id, actor=actor + ) # failed to add - handle error? return None @@ -194,14 +201,7 @@ class MCPManager: """Update an MCP server by its name.""" mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor) if not mcp_server_id: - raise HTTPException( - status_code=404, - detail={ - "code": "MCPServerNotFoundError", - "message": f"MCP server {mcp_server_name} not found", - "mcp_server_name": mcp_server_name, - }, - ) + raise ValueError(f"MCP server {mcp_server_name} not found") return await self.update_mcp_server_by_id(mcp_server_id, mcp_server_update, actor) @enforce_types @@ -223,6 +223,18 @@ class MCPManager: # Convert the SQLAlchemy Tool object to PydanticTool return mcp_server.to_pydantic() + @enforce_types + async def get_mcp_servers_by_ids(self, mcp_server_ids: List[str], actor: PydanticUser) -> List[MCPServer]: + """Fetch multiple MCP servers by their IDs in a single query.""" + if not mcp_server_ids: + return [] + + async with db_registry.async_session() as session: + mcp_servers = await MCPServerModel.list_async( + db_session=session, organization_id=actor.organization_id, id=mcp_server_ids # This will use the IN operator + ) + return [mcp_server.to_pydantic() for mcp_server in mcp_servers] + @enforce_types async def get_mcp_server(self, mcp_server_name: str, actor: PydanticUser) -> PydanticTool: """Get a tool by name.""" @@ -230,14 +242,7 @@ class MCPManager: mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor) mcp_server = await MCPServerModel.read_async(db_session=session, identifier=mcp_server_id, actor=actor) if not mcp_server: - raise HTTPException( - status_code=404, # Not Found - detail={ - "code": "MCPServerNotFoundError", - "message": f"MCP server {mcp_server_name} not found", - "mcp_server_name": mcp_server_name, - }, - ) + raise ValueError(f"MCP server {mcp_server_name} not found") return mcp_server.to_pydantic() # @enforce_types diff --git a/letta/services/tool_manager.py b/letta/services/tool_manager.py index 1885fa24..29d2723a 100644 --- a/letta/services/tool_manager.py +++ b/letta/services/tool_manager.py @@ -106,8 +106,10 @@ class ToolManager: @enforce_types @trace_method - def create_or_update_mcp_tool(self, tool_create: ToolCreate, mcp_server_name: str, actor: PydanticUser) -> PydanticTool: - metadata = {MCP_TOOL_TAG_NAME_PREFIX: {"server_name": mcp_server_name}} + def create_or_update_mcp_tool( + self, tool_create: ToolCreate, mcp_server_name: str, mcp_server_id: str, actor: PydanticUser + ) -> PydanticTool: + metadata = {MCP_TOOL_TAG_NAME_PREFIX: {"server_name": mcp_server_name, "server_id": mcp_server_id}} return self.create_or_update_tool( PydanticTool( tool_type=ToolType.EXTERNAL_MCP, name=tool_create.json_schema["name"], metadata_=metadata, **tool_create.model_dump() @@ -116,8 +118,10 @@ class ToolManager: ) @enforce_types - async def create_mcp_tool_async(self, tool_create: ToolCreate, mcp_server_name: str, actor: PydanticUser) -> PydanticTool: - metadata = {MCP_TOOL_TAG_NAME_PREFIX: {"server_name": mcp_server_name}} + async def create_mcp_tool_async( + self, tool_create: ToolCreate, mcp_server_name: str, mcp_server_id: str, actor: PydanticUser + ) -> PydanticTool: + metadata = {MCP_TOOL_TAG_NAME_PREFIX: {"server_name": mcp_server_name, "server_id": mcp_server_id}} return await self.create_or_update_tool_async( PydanticTool( tool_type=ToolType.EXTERNAL_MCP, name=tool_create.json_schema["name"], metadata_=metadata, **tool_create.model_dump() diff --git a/tests/mcp/mcp_config.json b/tests/mcp/mcp_config.json index 0967ef42..9e26dfee 100644 --- a/tests/mcp/mcp_config.json +++ b/tests/mcp/mcp_config.json @@ -1 +1 @@ -{} +{} \ No newline at end of file diff --git a/tests/test_agent_serialization_v2.py b/tests/test_agent_serialization_v2.py index 5d0b8570..10f5480c 100644 --- a/tests/test_agent_serialization_v2.py +++ b/tests/test_agent_serialization_v2.py @@ -283,6 +283,68 @@ async def agent_with_files(server: SyncServer, default_user, test_block, weather return (agent_state.id, test_source.id, test_file.id) +@pytest.fixture +async def test_mcp_server(server: SyncServer, default_user): + """Fixture to create and return a test MCP server.""" + from letta.schemas.mcp import MCPServer, MCPServerType + + mcp_server_data = MCPServer( + server_name="test_mcp_server", + server_type=MCPServerType.SSE, + server_url="http://test-mcp-server.com", + token="test-token-12345", # This should be excluded during export + custom_headers={"X-API-Key": "secret-key"}, # This should be excluded during export + ) + mcp_server = await server.mcp_manager.create_or_update_mcp_server(mcp_server_data, default_user) + yield mcp_server + + +@pytest.fixture +async def mcp_tool(server: SyncServer, default_user, test_mcp_server): + """Fixture to create and return an MCP tool.""" + from letta.schemas.tool import MCPTool, ToolCreate + + # Create a mock MCP tool + mcp_tool_data = MCPTool( + name="test_mcp_tool", + description="Test MCP tool for serialization", + inputSchema={"type": "object", "properties": {"input": {"type": "string"}}}, + ) + tool_create = ToolCreate.from_mcp(test_mcp_server.server_name, mcp_tool_data) + + # Create tool with MCP metadata + mcp_tool = await server.tool_manager.create_mcp_tool_async(tool_create, test_mcp_server.server_name, test_mcp_server.id, default_user) + yield mcp_tool + + +@pytest.fixture +async def agent_with_mcp_tools(server: SyncServer, default_user, test_block, mcp_tool, test_mcp_server): + """Fixture to create and return an agent with MCP tools.""" + memory_blocks = [ + CreateBlock(label="human", value="User is a test user"), + CreateBlock(label="persona", value="I am a helpful test assistant"), + ] + + create_agent_request = CreateAgent( + name="test_agent_mcp", + system="You are a helpful assistant with MCP tools.", + memory_blocks=memory_blocks, + llm_config=LLMConfig.default_config("gpt-4o-mini"), + embedding_config=EmbeddingConfig.default_config(provider="openai"), + block_ids=[test_block.id], + tool_ids=[mcp_tool.id], + tags=["test", "mcp", "export"], + description="Test agent with MCP tools for serialization testing", + ) + + agent_state = await server.agent_manager.create_agent_async( + agent_create=create_agent_request, + actor=default_user, + ) + + return agent_state + + # ------------------------------ # Helper Functions # ------------------------------ @@ -1258,7 +1320,7 @@ class TestAgentFileValidation: files=[], sources=[], tools=[], - # mcp_servers=[], + mcp_servers=[], ) # Should not raise @@ -1302,7 +1364,7 @@ class TestAgentFileValidation: json_schema={"name": "test_tool", "parameters": {"type": "object", "properties": {}}}, ) ], - # mcp_servers=[], + mcp_servers=[], ) assert validate_id_format(valid_schema) @@ -1316,11 +1378,282 @@ class TestAgentFileValidation: files=[], sources=[], tools=[], - # mcp_servers=[], + mcp_servers=[], ) assert not validate_id_format(invalid_schema) +class TestMCPServerSerialization: + """Tests for MCP server export/import functionality.""" + + async def test_mcp_server_export(self, agent_serialization_manager, agent_with_mcp_tools, default_user): + """Test that MCP servers are exported correctly.""" + agent_file = await agent_serialization_manager.export([agent_with_mcp_tools.id], default_user) + + # Verify MCP server is included + assert len(agent_file.mcp_servers) == 1 + mcp_server = agent_file.mcp_servers[0] + + # Verify server details + assert mcp_server.server_name == "test_mcp_server" + assert mcp_server.server_url == "http://test-mcp-server.com" + assert mcp_server.server_type == "sse" + + # Verify auth fields are excluded + assert not hasattr(mcp_server, "token") + assert not hasattr(mcp_server, "custom_headers") + + # Verify ID format + assert _validate_entity_id(mcp_server.id, "mcp_server") + + async def test_mcp_server_auth_scrubbing(self, server, agent_serialization_manager, default_user): + """Test that authentication information is scrubbed during export.""" + from letta.schemas.mcp import MCPServer, MCPServerType + + # Create MCP server with auth info + mcp_server_data_stdio = MCPServer( + server_name="auth_test_server", + server_type=MCPServerType.STDIO, + # token="super-secret-token", + # custom_headers={"Authorization": "Bearer secret-key", "X-Custom": "custom-value"}, + stdio_config={ + "server_name": "auth_test_server", + "command": "test-command", + "args": ["arg1", "arg2"], + "env": {"ENV_VAR": "value"}, + }, + ) + mcp_server = await server.mcp_manager.create_or_update_mcp_server(mcp_server_data_stdio, default_user) + + mcp_server_data_http = MCPServer( + server_name="auth_test_server_http", + server_type=MCPServerType.STREAMABLE_HTTP, + server_url="http://auth_test_server_http.com", + token="super-secret-token", + custom_headers={"X-Custom": "custom-value"}, + ) + mcp_server_http = await server.mcp_manager.create_or_update_mcp_server(mcp_server_data_http, default_user) + # Create tool from MCP server + from letta.schemas.tool import MCPTool, ToolCreate + + mcp_tool_data = MCPTool( + name="auth_test_tool_stdio", + description="Tool with auth", + inputSchema={"type": "object", "properties": {}}, + ) + tool_create_stdio = ToolCreate.from_mcp(mcp_server.server_name, mcp_tool_data) + + mcp_tool_data_http = MCPTool( + name="auth_test_tool_http", + description="Tool with auth", + inputSchema={"type": "object", "properties": {}}, + ) + + tool_create_http = ToolCreate.from_mcp(mcp_server_http.server_name, mcp_tool_data_http) + + mcp_tool = await server.tool_manager.create_mcp_tool_async(tool_create_stdio, mcp_server.server_name, mcp_server.id, default_user) + mcp_tool_http = await server.tool_manager.create_mcp_tool_async( + tool_create_http, mcp_server_http.server_name, mcp_server_http.id, default_user + ) + + # Create agent with the tool + from letta.schemas.agent import CreateAgent + + create_agent_request = CreateAgent( + name="auth_test_agent", + tool_ids=[mcp_tool.id, mcp_tool_http.id], + llm_config=LLMConfig.default_config("gpt-4o-mini"), + embedding_config=EmbeddingConfig.default_config(provider="openai"), + ) + agent = await server.agent_manager.create_agent_async(create_agent_request, default_user) + + # Export + agent_file = await agent_serialization_manager.export([agent.id], default_user) + + for server in agent_file.mcp_servers: + if server.server_name == "auth_test_server": + exported_server_stdio = server + elif server.server_name == "auth_test_server_http": + exported_server_http = server + + # Verify env variables in stdio server are excluded (typically used for auth) + assert exported_server_stdio.id != mcp_server.id + assert exported_server_stdio.server_name == "auth_test_server" + assert exported_server_stdio.stdio_config == { + "server_name": "auth_test_server", + "type": "stdio", + "command": "test-command", + "args": ["arg1", "arg2"], + } # Non-auth config preserved + assert exported_server_stdio.server_type == "stdio" + + # Verify token and custom headers are excluded from export for http server + assert exported_server_http.id != mcp_server_http.id + assert exported_server_http.server_name == "auth_test_server_http" + assert exported_server_http.server_type == "streamable_http" + assert exported_server_http.server_url == "http://auth_test_server_http.com" + assert not hasattr(exported_server_http, "token") + assert not hasattr(exported_server_http, "custom_headers") + + async def test_mcp_tool_metadata_with_server_id(self, agent_serialization_manager, agent_with_mcp_tools, default_user): + """Test that MCP tools have server_id in metadata.""" + agent_file = await agent_serialization_manager.export([agent_with_mcp_tools.id], default_user) + + # Find the MCP tool + mcp_tool = next((t for t in agent_file.tools if t.name == "test_mcp_tool"), None) + assert mcp_tool is not None + + # Verify metadata contains server info + assert mcp_tool.metadata_ is not None + assert "mcp" in mcp_tool.metadata_ + assert "server_name" in mcp_tool.metadata_["mcp"] + assert "server_id" in mcp_tool.metadata_["mcp"] + assert mcp_tool.metadata_["mcp"]["server_name"] == "test_mcp_server" + + # Verify tag format + assert any(tag.startswith("mcp:") for tag in mcp_tool.tags) + + async def test_mcp_server_import(self, agent_serialization_manager, agent_with_mcp_tools, default_user, other_user): + """Test importing agents with MCP servers.""" + # Export from default user + agent_file = await agent_serialization_manager.export([agent_with_mcp_tools.id], default_user) + + # Import to other user + result = await agent_serialization_manager.import_file(agent_file, other_user) + + assert result.success + + # Verify MCP server was imported + mcp_server_id = next((db_id for file_id, db_id in result.id_mappings.items() if file_id.startswith("mcp_server-")), None) + assert mcp_server_id is not None + + async def test_multiple_mcp_servers_export(self, server, agent_serialization_manager, default_user): + """Test exporting multiple MCP servers from different agents.""" + from letta.schemas.mcp import MCPServer, MCPServerType + + # Create two MCP servers + mcp_server1 = await server.mcp_manager.create_or_update_mcp_server( + MCPServer( + server_name="mcp1", + server_type=MCPServerType.STREAMABLE_HTTP, + server_url="http://mcp1.com", + token="super-secret-token", + custom_headers={"X-Custom": "custom-value"}, + ), + default_user, + ) + mcp_server2 = await server.mcp_manager.create_or_update_mcp_server( + MCPServer( + server_name="mcp2", + server_type=MCPServerType.STDIO, + stdio_config={ + "server_name": "mcp2", + "command": "mcp2-cmd", + "args": ["arg1", "arg2"], + }, + ), + default_user, + ) + + # Create tools from each server + from letta.schemas.tool import MCPTool, ToolCreate + + tool1 = await server.tool_manager.create_mcp_tool_async( + ToolCreate.from_mcp( + "mcp1", + MCPTool(name="tool1", description="Tool 1", inputSchema={"type": "object", "properties": {}}), + ), + "mcp1", + mcp_server1.id, + default_user, + ) + tool2 = await server.tool_manager.create_mcp_tool_async( + ToolCreate.from_mcp( + "mcp2", + MCPTool(name="tool2", description="Tool 2", inputSchema={"type": "object", "properties": {}}), + ), + "mcp2", + mcp_server2.id, + default_user, + ) + + # Create agents with different MCP tools + from letta.schemas.agent import CreateAgent + + agent1 = await server.agent_manager.create_agent_async( + CreateAgent( + name="agent1", + tool_ids=[tool1.id], + llm_config=LLMConfig.default_config("gpt-4o-mini"), + embedding_config=EmbeddingConfig.default_config(provider="openai"), + ), + default_user, + ) + agent2 = await server.agent_manager.create_agent_async( + CreateAgent( + name="agent2", + tool_ids=[tool2.id], + llm_config=LLMConfig.default_config("gpt-4o-mini"), + embedding_config=EmbeddingConfig.default_config(provider="openai"), + ), + default_user, + ) + + # Export both agents + agent_file = await agent_serialization_manager.export([agent1.id, agent2.id], default_user) + + # Verify both MCP servers are included + assert len(agent_file.mcp_servers) == 2 + + # Verify server types + streamable_http_server = next(s for s in agent_file.mcp_servers if s.server_name == "mcp1") + stdio_server = next(s for s in agent_file.mcp_servers if s.server_name == "mcp2") + + assert streamable_http_server.server_name == "mcp1" + assert streamable_http_server.server_type == "streamable_http" + assert streamable_http_server.server_url == "http://mcp1.com" + + assert stdio_server.server_name == "mcp2" + assert stdio_server.server_type == "stdio" + assert stdio_server.stdio_config == { + "server_name": "mcp2", + "type": "stdio", + "command": "mcp2-cmd", + "args": ["arg1", "arg2"], + } + + async def test_mcp_server_deduplication(self, server, agent_serialization_manager, default_user, test_mcp_server, mcp_tool): + """Test that shared MCP servers are deduplicated during export.""" + # Create two agents using the same MCP tool + from letta.schemas.agent import CreateAgent + + agent1 = await server.agent_manager.create_agent_async( + CreateAgent( + name="agent_dup1", + tool_ids=[mcp_tool.id], + llm_config=LLMConfig.default_config("gpt-4o-mini"), + embedding_config=EmbeddingConfig.default_config(provider="openai"), + ), + default_user, + ) + agent2 = await server.agent_manager.create_agent_async( + CreateAgent( + name="agent_dup2", + tool_ids=[mcp_tool.id], + llm_config=LLMConfig.default_config("gpt-4o-mini"), + embedding_config=EmbeddingConfig.default_config(provider="openai"), + ), + default_user, + ) + + # Export both agents + agent_file = await agent_serialization_manager.export([agent1.id, agent2.id], default_user) + + # Verify only one MCP server is exported + assert len(agent_file.mcp_servers) == 1 + assert agent_file.mcp_servers[0].server_name == "test_mcp_server" + + if __name__ == "__main__": pytest.main([__file__]) diff --git a/tests/test_managers.py b/tests/test_managers.py index c75b6167..395f3ebc 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -269,8 +269,11 @@ def mcp_tool(server, default_user): }, ) mcp_server_name = "test" + mcp_server_id = "test-server-id" # Mock server ID for testing tool_create = ToolCreate.from_mcp(mcp_server_name=mcp_server_name, mcp_tool=mcp_tool) - tool = server.tool_manager.create_or_update_mcp_tool(tool_create=tool_create, mcp_server_name=mcp_server_name, actor=default_user) + tool = server.tool_manager.create_or_update_mcp_tool( + tool_create=tool_create, mcp_server_name=mcp_server_name, mcp_server_id=mcp_server_id, actor=default_user + ) yield tool @@ -3474,6 +3477,7 @@ def test_create_mcp_tool(server: SyncServer, mcp_tool, default_user, default_org assert mcp_tool.created_by_id == default_user.id assert mcp_tool.tool_type == ToolType.EXTERNAL_MCP assert mcp_tool.metadata_[MCP_TOOL_TAG_NAME_PREFIX]["server_name"] == "test" + assert mcp_tool.metadata_[MCP_TOOL_TAG_NAME_PREFIX]["server_id"] == "test-server-id" # Test should work with both SQLite and PostgreSQL @@ -8552,6 +8556,83 @@ async def test_create_mcp_server(server, default_user, event_loop): print("TAGS", tool.tags) +async def test_get_mcp_servers_by_ids(server, default_user, event_loop): + from letta.schemas.mcp import MCPServer, MCPServerType, SSEServerConfig, StdioServerConfig + from letta.settings import tool_settings + + if tool_settings.mcp_read_from_config: + return + + # Create multiple MCP servers for testing + servers_data = [ + { + "name": "test_server_1", + "config": StdioServerConfig( + server_name="test_server_1", type=MCPServerType.STDIO, command="echo 'test1'", args=["arg1"], env={"ENV1": "value1"} + ), + "type": MCPServerType.STDIO, + }, + { + "name": "test_server_2", + "config": SSEServerConfig(server_name="test_server_2", server_url="https://test2.example.com/sse"), + "type": MCPServerType.SSE, + }, + { + "name": "test_server_3", + "config": SSEServerConfig(server_name="test_server_3", server_url="https://test3.example.com/sse"), + "type": MCPServerType.SSE, + }, + ] + + created_servers = [] + for server_data in servers_data: + if server_data["type"] == MCPServerType.STDIO: + mcp_server = MCPServer(server_name=server_data["name"], server_type=server_data["type"], stdio_config=server_data["config"]) + else: + mcp_server = MCPServer( + server_name=server_data["name"], server_type=server_data["type"], server_url=server_data["config"].server_url + ) + + created = await server.mcp_manager.create_or_update_mcp_server(mcp_server, actor=default_user) + created_servers.append(created) + + # Test fetching multiple servers by IDs + server_ids = [s.id for s in created_servers] + fetched_servers = await server.mcp_manager.get_mcp_servers_by_ids(server_ids, actor=default_user) + + assert len(fetched_servers) == len(created_servers) + fetched_ids = {s.id for s in fetched_servers} + expected_ids = {s.id for s in created_servers} + assert fetched_ids == expected_ids + + # Test fetching subset of servers + subset_ids = server_ids[:2] + subset_servers = await server.mcp_manager.get_mcp_servers_by_ids(subset_ids, actor=default_user) + assert len(subset_servers) == 2 + assert all(s.id in subset_ids for s in subset_servers) + + # Test fetching with empty list + empty_result = await server.mcp_manager.get_mcp_servers_by_ids([], actor=default_user) + assert empty_result == [] + + # Test fetching with non-existent ID mixed with valid IDs + mixed_ids = [server_ids[0], "non-existent-id", server_ids[1]] + mixed_result = await server.mcp_manager.get_mcp_servers_by_ids(mixed_ids, actor=default_user) + # Should only return the existing servers + assert len(mixed_result) == 2 + assert all(s.id in server_ids for s in mixed_result) + + # Test that servers from different organizations are not returned + # This would require creating another user/org, but for now we'll just verify + # that the function respects the actor's organization + all_servers = await server.mcp_manager.list_mcp_servers(actor=default_user) + all_server_ids = [s.id for s in all_servers] + bulk_fetched = await server.mcp_manager.get_mcp_servers_by_ids(all_server_ids, actor=default_user) + + # All fetched servers should belong to the same organization + assert all(s.organization_id == default_user.organization_id for s in bulk_fetched) + + # ====================================================================================================================== # FileAgent Tests # ====================================================================================================================== From 772a51777e2454d61817e14f32315174740d8e30 Mon Sep 17 00:00:00 2001 From: jnjpng Date: Thu, 24 Jul 2025 18:23:01 -0700 Subject: [PATCH 12/24] feat: add support for oauth mcp Co-authored-by: Jin Peng --- .../versions/f5d26b0526e8_add_mcp_oauth.py | 67 +++ letta/orm/mcp_oauth.py | 62 +++ letta/schemas/mcp.py | 70 +++ letta/server/rest_api/routers/v1/tools.py | 237 +++++++++- letta/services/mcp/base_client.py | 48 +- letta/services/mcp/oauth_utils.py | 433 ++++++++++++++++++ letta/services/mcp/sse_client.py | 13 +- letta/services/mcp/streamable_http_client.py | 22 +- letta/services/mcp/types.py | 9 + letta/services/mcp_manager.py | 297 ++++++++++-- mcp_test.py | 356 ++++++++++++++ 11 files changed, 1529 insertions(+), 85 deletions(-) create mode 100644 alembic/versions/f5d26b0526e8_add_mcp_oauth.py create mode 100644 letta/orm/mcp_oauth.py create mode 100644 letta/services/mcp/oauth_utils.py create mode 100644 mcp_test.py diff --git a/alembic/versions/f5d26b0526e8_add_mcp_oauth.py b/alembic/versions/f5d26b0526e8_add_mcp_oauth.py new file mode 100644 index 00000000..52c9f764 --- /dev/null +++ b/alembic/versions/f5d26b0526e8_add_mcp_oauth.py @@ -0,0 +1,67 @@ +"""add_mcp_oauth + +Revision ID: f5d26b0526e8 +Revises: ddecfe4902bc +Create Date: 2025-07-24 12:34:05.795355 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "f5d26b0526e8" +down_revision: Union[str, None] = "ddecfe4902bc" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "mcp_oauth", + sa.Column("id", sa.String(), nullable=False), + sa.Column("state", sa.String(length=255), nullable=False), + sa.Column("server_id", sa.String(length=255), nullable=True), + sa.Column("server_url", sa.Text(), nullable=False), + sa.Column("server_name", sa.Text(), nullable=False), + sa.Column("authorization_url", sa.Text(), nullable=True), + sa.Column("authorization_code", sa.Text(), nullable=True), + sa.Column("access_token", sa.Text(), nullable=True), + sa.Column("refresh_token", sa.Text(), nullable=True), + sa.Column("token_type", sa.String(length=50), nullable=False), + sa.Column("expires_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("scope", sa.Text(), nullable=True), + sa.Column("client_id", sa.Text(), nullable=True), + sa.Column("client_secret", sa.Text(), nullable=True), + sa.Column("redirect_uri", sa.Text(), nullable=True), + sa.Column("status", sa.String(length=20), nullable=False), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("is_deleted", sa.Boolean(), server_default=sa.text("FALSE"), nullable=False), + sa.Column("_created_by_id", sa.String(), nullable=True), + sa.Column("_last_updated_by_id", sa.String(), nullable=True), + sa.Column("organization_id", sa.String(), nullable=False), + sa.Column("user_id", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["organization_id"], + ["organizations.id"], + ), + sa.ForeignKeyConstraint(["server_id"], ["mcp_server.id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint( + ["user_id"], + ["users.id"], + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("state"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("mcp_oauth") + # ### end Alembic commands ### diff --git a/letta/orm/mcp_oauth.py b/letta/orm/mcp_oauth.py new file mode 100644 index 00000000..e34f685a --- /dev/null +++ b/letta/orm/mcp_oauth.py @@ -0,0 +1,62 @@ +import uuid +from datetime import datetime +from enum import Enum +from typing import Optional + +from sqlalchemy import DateTime, ForeignKey, String, Text +from sqlalchemy.orm import Mapped, mapped_column + +from letta.orm.mixins import OrganizationMixin, UserMixin +from letta.orm.sqlalchemy_base import SqlalchemyBase + + +class OAuthSessionStatus(str, Enum): + """OAuth session status enumeration.""" + + PENDING = "pending" + AUTHORIZED = "authorized" + ERROR = "error" + + +class MCPOAuth(SqlalchemyBase, OrganizationMixin, UserMixin): + """OAuth session model for MCP server authentication.""" + + __tablename__ = "mcp_oauth" + + # Override the id field to match database UUID generation + id: Mapped[str] = mapped_column(String, primary_key=True, default=lambda: f"{uuid.uuid4()}") + + # Core session information + state: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, doc="OAuth state parameter") + server_id: Mapped[str] = mapped_column(String(255), ForeignKey("mcp_server.id", ondelete="CASCADE"), nullable=True, doc="MCP server ID") + server_url: Mapped[str] = mapped_column(Text, nullable=False, doc="MCP server URL") + server_name: Mapped[str] = mapped_column(Text, nullable=False, doc="MCP server display name") + + # OAuth flow data + authorization_url: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth authorization URL") + authorization_code: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth authorization code") + + # Token data + access_token: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth access token") + refresh_token: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth refresh token") + token_type: Mapped[str] = mapped_column(String(50), default="Bearer", doc="Token type") + expires_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True, doc="Token expiry time") + scope: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth scope") + + # Client configuration + client_id: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth client ID") + client_secret: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth client secret") + redirect_uri: Mapped[Optional[str]] = mapped_column(Text, nullable=True, doc="OAuth redirect URI") + + # Session state + status: Mapped[OAuthSessionStatus] = mapped_column(String(20), default=OAuthSessionStatus.PENDING, doc="Session status") + + # Timestamps + created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(), doc="Session creation time") + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), default=lambda: datetime.now(), onupdate=lambda: datetime.now(), doc="Last update time" + ) + + # Relationships (if needed in the future) + # user: Mapped[Optional["User"]] = relationship("User", back_populates="oauth_sessions") + # organization: Mapped["Organization"] = relationship("Organization", back_populates="oauth_sessions") diff --git a/letta/schemas/mcp.py b/letta/schemas/mcp.py index b851f2d5..f7070e8b 100644 --- a/letta/schemas/mcp.py +++ b/letta/schemas/mcp.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Any, Dict, Optional, Union from pydantic import Field @@ -10,6 +11,7 @@ from letta.functions.mcp_client.types import ( StdioServerConfig, StreamableHTTPServerConfig, ) +from letta.orm.mcp_oauth import OAuthSessionStatus from letta.schemas.letta_base import LettaBase @@ -119,3 +121,71 @@ class UpdateStreamableHTTPMCPServer(LettaBase): UpdateMCPServer = Union[UpdateSSEMCPServer, UpdateStdioMCPServer, UpdateStreamableHTTPMCPServer] RegisterMCPServer = Union[RegisterSSEMCPServer, RegisterStdioMCPServer, RegisterStreamableHTTPMCPServer] + + +# OAuth-related schemas +class BaseMCPOAuth(LettaBase): + __id_prefix__ = "mcp-oauth" + + +class MCPOAuthSession(BaseMCPOAuth): + """OAuth session for MCP server authentication.""" + + id: str = BaseMCPOAuth.generate_id_field() + state: str = Field(..., description="OAuth state parameter") + server_id: Optional[str] = Field(None, description="MCP server ID") + server_url: str = Field(..., description="MCP server URL") + server_name: str = Field(..., description="MCP server display name") + + # User and organization context + user_id: Optional[str] = Field(None, description="User ID associated with the session") + organization_id: str = Field(..., description="Organization ID associated with the session") + + # OAuth flow data + authorization_url: Optional[str] = Field(None, description="OAuth authorization URL") + authorization_code: Optional[str] = Field(None, description="OAuth authorization code") + + # Token data + access_token: Optional[str] = Field(None, description="OAuth access token") + refresh_token: Optional[str] = Field(None, description="OAuth refresh token") + token_type: str = Field(default="Bearer", description="Token type") + expires_at: Optional[datetime] = Field(None, description="Token expiry time") + scope: Optional[str] = Field(None, description="OAuth scope") + + # Client configuration + client_id: Optional[str] = Field(None, description="OAuth client ID") + client_secret: Optional[str] = Field(None, description="OAuth client secret") + redirect_uri: Optional[str] = Field(None, description="OAuth redirect URI") + + # Session state + status: OAuthSessionStatus = Field(default=OAuthSessionStatus.PENDING, description="Session status") + + # Timestamps + created_at: datetime = Field(default_factory=datetime.now, description="Session creation time") + updated_at: datetime = Field(default_factory=datetime.now, description="Last update time") + + +class MCPOAuthSessionCreate(BaseMCPOAuth): + """Create a new OAuth session.""" + + server_url: str = Field(..., description="MCP server URL") + server_name: str = Field(..., description="MCP server display name") + user_id: Optional[str] = Field(None, description="User ID associated with the session") + organization_id: str = Field(..., description="Organization ID associated with the session") + state: Optional[str] = Field(None, description="OAuth state parameter") + + +class MCPOAuthSessionUpdate(BaseMCPOAuth): + """Update an existing OAuth session.""" + + authorization_url: Optional[str] = Field(None, description="OAuth authorization URL") + authorization_code: Optional[str] = Field(None, description="OAuth authorization code") + access_token: Optional[str] = Field(None, description="OAuth access token") + refresh_token: Optional[str] = Field(None, description="OAuth refresh token") + token_type: Optional[str] = Field(None, description="Token type") + expires_at: Optional[datetime] = Field(None, description="Token expiry time") + scope: Optional[str] = Field(None, description="OAuth scope") + client_id: Optional[str] = Field(None, description="OAuth client ID") + client_secret: Optional[str] = Field(None, description="OAuth client secret") + redirect_uri: Optional[str] = Field(None, description="OAuth redirect URI") + status: Optional[OAuthSessionStatus] = Field(None, description="Session status") diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index ad171613..354afdc2 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -1,4 +1,6 @@ +import asyncio import json +from collections.abc import AsyncGenerator from typing import Any, Dict, List, Optional, Union from composio.client import ComposioClientError, HTTPError, NoItemsFound @@ -11,27 +13,37 @@ from composio.exceptions import ( EnumStringNotFound, ) from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query +from fastapi.responses import HTMLResponse from pydantic import BaseModel, Field +from starlette.responses import StreamingResponse from letta.errors import LettaToolCreateError from letta.functions.functions import derive_openai_json_schema from letta.functions.mcp_client.exceptions import MCPTimeoutError -from letta.functions.mcp_client.types import MCPServerType, MCPTool, SSEServerConfig, StdioServerConfig, StreamableHTTPServerConfig +from letta.functions.mcp_client.types import MCPTool, SSEServerConfig, StdioServerConfig, StreamableHTTPServerConfig from letta.helpers.composio_helpers import get_composio_api_key +from letta.helpers.decorators import deprecated from letta.llm_api.llm_client import LLMClient from letta.log import get_logger from letta.orm.errors import UniqueConstraintViolationError +from letta.orm.mcp_oauth import OAuthSessionStatus from letta.schemas.enums import MessageRole from letta.schemas.letta_message import ToolReturnMessage from letta.schemas.letta_message_content import TextContent -from letta.schemas.mcp import UpdateSSEMCPServer, UpdateStdioMCPServer, UpdateStreamableHTTPMCPServer +from letta.schemas.mcp import MCPOAuthSessionCreate, UpdateSSEMCPServer, UpdateStdioMCPServer, UpdateStreamableHTTPMCPServer from letta.schemas.message import Message from letta.schemas.tool import Tool, ToolCreate, ToolRunFromSource, ToolUpdate +from letta.server.rest_api.streaming_response import StreamingResponseWithStatusCode from letta.server.rest_api.utils import get_letta_server from letta.server.server import SyncServer -from letta.services.mcp.sse_client import AsyncSSEMCPClient -from letta.services.mcp.stdio_client import AsyncStdioMCPClient -from letta.services.mcp.streamable_http_client import AsyncStreamableHTTPMCPClient +from letta.services.mcp.oauth_utils import ( + MCPOAuthSession, + create_oauth_provider, + drill_down_exception, + get_oauth_success_html, + oauth_stream_event, +) +from letta.services.mcp.types import OauthStreamEvent from letta.settings import tool_settings router = APIRouter(prefix="/tools", tags=["tools"]) @@ -612,35 +624,26 @@ async def delete_mcp_server_from_config( return [server.to_config() for server in all_servers] -@router.post("/mcp/servers/test", response_model=List[MCPTool], operation_id="test_mcp_server") +@deprecated("Deprecated in favor of /mcp/servers/connect which handles OAuth flow via SSE stream") +@router.post("/mcp/servers/test", operation_id="test_mcp_server") async def test_mcp_server( request: Union[StdioServerConfig, SSEServerConfig, StreamableHTTPServerConfig] = Body(...), + server: SyncServer = Depends(get_letta_server), + actor_id: Optional[str] = Header(None, alias="user_id"), ): """ Test connection to an MCP server without adding it. - Returns the list of available tools if successful. + Returns the list of available tools if successful, or OAuth information if OAuth is required. """ client = None try: - # create a temporary MCP client based on the server type - if request.type == MCPServerType.SSE: - if not isinstance(request, SSEServerConfig): - request = SSEServerConfig(**request.model_dump()) - client = AsyncSSEMCPClient(request) - elif request.type == MCPServerType.STREAMABLE_HTTP: - if not isinstance(request, StreamableHTTPServerConfig): - request = StreamableHTTPServerConfig(**request.model_dump()) - client = AsyncStreamableHTTPMCPClient(request) - elif request.type == MCPServerType.STDIO: - if not isinstance(request, StdioServerConfig): - request = StdioServerConfig(**request.model_dump()) - client = AsyncStdioMCPClient(request) - else: - raise ValueError(f"Invalid MCP server type: {request.type}") + actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id) + client = await server.mcp_manager.get_mcp_client(request, actor) await client.connect_to_server() tools = await client.list_tools() - return tools + + return {"status": "success", "tools": tools} except ConnectionError as e: raise HTTPException( status_code=400, @@ -676,6 +679,155 @@ async def test_mcp_server( logger.warning(f"Error during MCP client cleanup: {cleanup_error}") +@router.post( + "/mcp/servers/connect", + response_model=None, + responses={ + 200: { + "description": "Successful response", + "content": { + "text/event-stream": {"description": "Server-Sent Events stream"}, + }, + } + }, + operation_id="connect_mcp_server", +) +async def connect_mcp_server( + request: Union[StdioServerConfig, SSEServerConfig, StreamableHTTPServerConfig] = Body(...), + server: SyncServer = Depends(get_letta_server), + actor_id: Optional[str] = Header(None, alias="user_id"), +) -> StreamingResponse: + """ + Connect to an MCP server with support for OAuth via SSE. + Returns a stream of events handling authorization state and exchange if OAuth is required. + """ + + async def oauth_stream_generator( + request: Union[StdioServerConfig, SSEServerConfig, StreamableHTTPServerConfig] + ) -> AsyncGenerator[str, None]: + client = None + oauth_provider = None + temp_client = None + connect_task = None + + try: + # Acknolwedge connection attempt + yield oauth_stream_event(OauthStreamEvent.CONNECTION_ATTEMPT, server_name=request.server_name) + + actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id) + + # Create MCP client with respective transport type + try: + client = await server.mcp_manager.get_mcp_client(request, actor) + except ValueError as e: + yield oauth_stream_event(OauthStreamEvent.ERROR, message=str(e)) + return + + # Try normal connection first for flows that don't require OAuth + try: + await client.connect_to_server() + tools = await client.list_tools(serialize=True) + yield oauth_stream_event(OauthStreamEvent.SUCCESS, tools=tools) + return + except ConnectionError: + # Continue to OAuth flow + logger.info(f"Attempting OAuth flow for {request.server_url}...") + except Exception as e: + yield oauth_stream_event(OauthStreamEvent.ERROR, message=f"Connection failed: {str(e)}") + return + + # OAuth required, yield state to client to prepare to handle authorization URL + yield oauth_stream_event(OauthStreamEvent.OAUTH_REQUIRED, message="OAuth authentication required") + + # Create OAuth session to persist the state of the OAuth flow + session_create = MCPOAuthSessionCreate( + server_url=request.server_url, + server_name=request.server_name, + user_id=actor.id, + organization_id=actor.organization_id, + ) + oauth_session = await server.mcp_manager.create_oauth_session(session_create, actor) + session_id = oauth_session.id + + # Create OAuth provider for the instance of the stream connection + # Note: Using the correct API path for the callback + # do not edit this this is the correct url + redirect_uri = f"http://localhost:8283/v1/tools/mcp/oauth/callback/{session_id}" + oauth_provider = await create_oauth_provider(session_id, request.server_url, redirect_uri, server.mcp_manager, actor) + + # Get authorization URL by triggering OAuth flow + temp_client = None + try: + temp_client = await server.mcp_manager.get_mcp_client(request, actor, oauth_provider) + + # Run connect_to_server in background to avoid blocking + # This will trigger the OAuth flow and the redirect_handler will save the authorization URL to database + connect_task = asyncio.create_task(temp_client.connect_to_server()) + + # Give the OAuth flow time to trigger and save the URL + await asyncio.sleep(1.0) + + # Fetch the authorization URL from database and yield state to client to proceed with handling authorization URL + auth_session = await server.mcp_manager.get_oauth_session_by_id(session_id, actor) + if auth_session and auth_session.authorization_url: + yield oauth_stream_event(OauthStreamEvent.AUTHORIZATION_URL, url=auth_session.authorization_url, session_id=session_id) + + except Exception as e: + logger.error(f"Error triggering OAuth flow: {e}") + yield oauth_stream_event(OauthStreamEvent.ERROR, message=f"Failed to trigger OAuth: {str(e)}") + + # Clean up active resources + if connect_task and not connect_task.done(): + connect_task.cancel() + try: + await connect_task + except asyncio.CancelledError: + pass + if temp_client: + try: + await temp_client.cleanup() + except Exception as cleanup_error: + logger.warning(f"Error during temp MCP client cleanup: {cleanup_error}") + return + + # Wait for user authorization (with timeout), client should render loading state until user completes the flow and /mcp/oauth/callback/{session_id} is hit + yield oauth_stream_event(OauthStreamEvent.WAITING_FOR_AUTH, message="Waiting for user authorization...") + + # Callback handler will poll for authorization code and state and update the OAuth session + await connect_task + + tools = await temp_client.list_tools(serialize=True) + + yield oauth_stream_event(OauthStreamEvent.SUCCESS, tools=tools) + return + except Exception as e: + detailed_error = drill_down_exception(e) + logger.error(f"Error in OAuth stream:\n{detailed_error}") + yield oauth_stream_event(OauthStreamEvent.ERROR, message=f"Internal error: {detailed_error}") + finally: + if connect_task and not connect_task.done(): + connect_task.cancel() + try: + await connect_task + except asyncio.CancelledError: + pass + if client: + try: + await client.cleanup() + except Exception as cleanup_error: + detailed_error = drill_down_exception(cleanup_error) + logger.warning(f"Error during MCP client cleanup: {detailed_error}") + if temp_client: + try: + await temp_client.cleanup() + except Exception as cleanup_error: + # TODO: @jnjpng fix async cancel scope issue + # detailed_error = drill_down_exception(cleanup_error) + logger.warning(f"Aysnc cleanup confict during temp MCP client cleanup: {cleanup_error}") + + return StreamingResponseWithStatusCode(oauth_stream_generator(request), media_type="text/event-stream") + + class CodeInput(BaseModel): code: str = Field(..., description="Python source code to parse for JSON schema") @@ -697,6 +849,45 @@ async def generate_json_schema( raise HTTPException(status_code=400, detail=f"Failed to generate schema: {str(e)}") +# TODO: @jnjpng need to route this through cloud API for production +@router.get("/mcp/oauth/callback/{session_id}", operation_id="mcp_oauth_callback", response_class=HTMLResponse) +async def mcp_oauth_callback( + session_id: str, + code: Optional[str] = Query(None, description="OAuth authorization code"), + state: Optional[str] = Query(None, description="OAuth state parameter"), + error: Optional[str] = Query(None, description="OAuth error"), + error_description: Optional[str] = Query(None, description="OAuth error description"), +): + """ + Handle OAuth callback for MCP server authentication. + """ + try: + oauth_session = MCPOAuthSession(session_id) + + if error: + error_msg = f"OAuth error: {error}" + if error_description: + error_msg += f" - {error_description}" + await oauth_session.update_session_status(OAuthSessionStatus.ERROR) + return {"status": "error", "message": error_msg} + + if not code or not state: + await oauth_session.update_session_status(OAuthSessionStatus.ERROR) + return {"status": "error", "message": "Missing authorization code or state"} + + # Store authorization code + success = await oauth_session.store_authorization_code(code, state) + if not success: + await oauth_session.update_session_status(OAuthSessionStatus.ERROR) + return {"status": "error", "message": "Invalid state parameter"} + + return HTMLResponse(content=get_oauth_success_html(), status_code=200) + + except Exception as e: + logger.error(f"OAuth callback error: {e}") + return {"status": "error", "message": f"OAuth callback failed: {str(e)}"} + + class GenerateToolInput(BaseModel): tool_name: str = Field(..., description="Name of the tool to generate code for") prompt: str = Field(..., description="User prompt to generate code") diff --git a/letta/services/mcp/base_client.py b/letta/services/mcp/base_client.py index 8aeda67f..0df3cba1 100644 --- a/letta/services/mcp/base_client.py +++ b/letta/services/mcp/base_client.py @@ -1,9 +1,9 @@ -import asyncio from contextlib import AsyncExitStack from typing import Optional, Tuple from mcp import ClientSession from mcp import Tool as MCPTool +from mcp.client.auth import OAuthClientProvider from mcp.types import TextContent from letta.functions.mcp_client.types import BaseServerConfig @@ -14,14 +14,12 @@ logger = get_logger(__name__) # TODO: Get rid of Async prefix on this class name once we deprecate old sync code class AsyncBaseMCPClient: - def __init__(self, server_config: BaseServerConfig): + def __init__(self, server_config: BaseServerConfig, oauth_provider: Optional[OAuthClientProvider] = None): self.server_config = server_config + self.oauth_provider = oauth_provider self.exit_stack = AsyncExitStack() self.session: Optional[ClientSession] = None self.initialized = False - # Track the task that created this client - self._creation_task = asyncio.current_task() - self._cleanup_queue = asyncio.Queue(maxsize=1) async def connect_to_server(self): try: @@ -48,9 +46,25 @@ class AsyncBaseMCPClient: async def _initialize_connection(self, server_config: BaseServerConfig) -> None: raise NotImplementedError("Subclasses must implement _initialize_connection") - async def list_tools(self) -> list[MCPTool]: + async def list_tools(self, serialize: bool = False) -> list[MCPTool]: self._check_initialized() response = await self.session.list_tools() + if serialize: + serializable_tools = [] + for tool in response.tools: + if hasattr(tool, "model_dump"): + # Pydantic model - use model_dump + serializable_tools.append(tool.model_dump()) + elif hasattr(tool, "dict"): + # Older Pydantic model - use dict() + serializable_tools.append(tool.dict()) + elif hasattr(tool, "__dict__"): + # Regular object - use __dict__ + serializable_tools.append(tool.__dict__) + else: + # Fallback - convert to string + serializable_tools.append(str(tool)) + return serializable_tools return response.tools async def execute_tool(self, tool_name: str, tool_args: dict) -> Tuple[str, bool]: @@ -79,29 +93,7 @@ class AsyncBaseMCPClient: # TODO: still hitting some async errors for voice agents, need to fix async def cleanup(self): - """Clean up resources - ensure this runs in the same task""" - if hasattr(self, "_cleanup_task"): - # If we're in a different task, schedule cleanup in original task - current_task = asyncio.current_task() - if current_task != self._creation_task: - # Create a future to signal completion - cleanup_done = asyncio.Future() - self._cleanup_queue.put_nowait((self.exit_stack, cleanup_done)) - await cleanup_done - return - - # Normal cleanup await self.exit_stack.aclose() def to_sync_client(self): raise NotImplementedError("Subclasses must implement to_sync_client") - - async def __aenter__(self): - """Enter the async context manager.""" - await self.connect_to_server() - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - """Exit the async context manager.""" - await self.cleanup() - return False # Don't suppress exceptions diff --git a/letta/services/mcp/oauth_utils.py b/letta/services/mcp/oauth_utils.py new file mode 100644 index 00000000..7391474c --- /dev/null +++ b/letta/services/mcp/oauth_utils.py @@ -0,0 +1,433 @@ +"""OAuth utilities for MCP server authentication.""" + +import asyncio +import json +import secrets +import time +import uuid +from datetime import datetime, timedelta +from typing import Callable, Optional, Tuple + +from mcp.client.auth import OAuthClientProvider, TokenStorage +from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken +from sqlalchemy import select + +from letta.log import get_logger +from letta.orm.mcp_oauth import MCPOAuth, OAuthSessionStatus +from letta.schemas.mcp import MCPOAuthSessionUpdate +from letta.schemas.user import User as PydanticUser +from letta.server.db import db_registry +from letta.services.mcp.types import OauthStreamEvent +from letta.services.mcp_manager import MCPManager + +logger = get_logger(__name__) + + +class DatabaseTokenStorage(TokenStorage): + """Database-backed token storage using MCPOAuth table via mcp_manager.""" + + def __init__(self, session_id: str, mcp_manager: MCPManager, actor: PydanticUser): + self.session_id = session_id + self.mcp_manager = mcp_manager + self.actor = actor + + async def get_tokens(self) -> Optional[OAuthToken]: + """Retrieve tokens from database.""" + oauth_session = await self.mcp_manager.get_oauth_session_by_id(self.session_id, self.actor) + if not oauth_session or not oauth_session.access_token: + return None + + return OAuthToken( + access_token=oauth_session.access_token, + refresh_token=oauth_session.refresh_token, + token_type=oauth_session.token_type, + expires_in=int(oauth_session.expires_at.timestamp() - time.time()), + scope=oauth_session.scope, + ) + + async def set_tokens(self, tokens: OAuthToken) -> None: + """Store tokens in database.""" + session_update = MCPOAuthSessionUpdate( + access_token=tokens.access_token, + refresh_token=tokens.refresh_token, + token_type=tokens.token_type, + expires_at=datetime.fromtimestamp(tokens.expires_in + time.time()), + scope=tokens.scope, + status=OAuthSessionStatus.AUTHORIZED, + ) + await self.mcp_manager.update_oauth_session(self.session_id, session_update, self.actor) + + async def get_client_info(self) -> Optional[OAuthClientInformationFull]: + """Retrieve client information from database.""" + oauth_session = await self.mcp_manager.get_oauth_session_by_id(self.session_id, self.actor) + if not oauth_session or not oauth_session.client_id: + return None + + return OAuthClientInformationFull( + client_id=oauth_session.client_id, + client_secret=oauth_session.client_secret, + redirect_uris=[oauth_session.redirect_uri] if oauth_session.redirect_uri else [], + ) + + async def set_client_info(self, client_info: OAuthClientInformationFull) -> None: + """Store client information in database.""" + session_update = MCPOAuthSessionUpdate( + client_id=client_info.client_id, + client_secret=client_info.client_secret, + redirect_uri=str(client_info.redirect_uris[0]) if client_info.redirect_uris else None, + ) + await self.mcp_manager.update_oauth_session(self.session_id, session_update, self.actor) + + +class MCPOAuthSession: + """Legacy OAuth session class - deprecated, use mcp_manager directly.""" + + def __init__(self, server_url: str, server_name: str, user_id: Optional[str], organization_id: str): + self.server_url = server_url + self.server_name = server_name + self.user_id = user_id + self.organization_id = organization_id + self.session_id = str(uuid.uuid4()) + self.state = secrets.token_urlsafe(32) + + def __init__(self, session_id: str): + self.session_id = session_id + + # TODO: consolidate / deprecate this in favor of mcp_manager access + async def create_session(self) -> str: + """Create a new OAuth session in the database.""" + async with db_registry.async_session() as session: + oauth_record = MCPOAuth( + id=self.session_id, + state=self.state, + server_url=self.server_url, + server_name=self.server_name, + user_id=self.user_id, + organization_id=self.organization_id, + status=OAuthSessionStatus.PENDING, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + oauth_record = await oauth_record.create_async(session, actor=None) + + return self.session_id + + async def get_session_status(self) -> OAuthSessionStatus: + """Get the current status of the OAuth session.""" + async with db_registry.async_session() as session: + try: + oauth_record = await MCPOAuth.read_async(db_session=session, identifier=self.session_id, actor=None) + return oauth_record.status + except Exception: + return OAuthSessionStatus.ERROR + + async def update_session_status(self, status: OAuthSessionStatus) -> None: + """Update the session status.""" + async with db_registry.async_session() as session: + try: + oauth_record = await MCPOAuth.read_async(db_session=session, identifier=self.session_id, actor=None) + oauth_record.status = status + oauth_record.updated_at = datetime.now() + await oauth_record.update_async(db_session=session, actor=None) + except Exception: + pass + + async def store_authorization_code(self, code: str, state: str) -> bool: + """Store the authorization code from OAuth callback.""" + async with db_registry.async_session() as session: + try: + oauth_record = await MCPOAuth.read_async(db_session=session, identifier=self.session_id, actor=None) + + # if oauth_record.state != state: + # return False + + oauth_record.authorization_code = code + oauth_record.state = state + oauth_record.status = OAuthSessionStatus.AUTHORIZED + oauth_record.updated_at = datetime.now() + await oauth_record.update_async(db_session=session, actor=None) + return True + except Exception: + return False + + async def get_authorization_url(self) -> Optional[str]: + """Get the authorization URL for this session.""" + async with db_registry.async_session() as session: + try: + oauth_record = await MCPOAuth.read_async(db_session=session, identifier=self.session_id, actor=None) + return oauth_record.authorization_url + except Exception: + return None + + async def set_authorization_url(self, url: str) -> None: + """Set the authorization URL for this session.""" + async with db_registry.async_session() as session: + try: + oauth_record = await MCPOAuth.read_async(db_session=session, identifier=self.session_id, actor=None) + oauth_record.authorization_url = url + oauth_record.updated_at = datetime.now() + await oauth_record.update_async(db_session=session, actor=None) + except Exception: + pass + + +async def create_oauth_provider( + session_id: str, + server_url: str, + redirect_uri: str, + mcp_manager: MCPManager, + actor: PydanticUser, + url_callback: Optional[Callable[[str], None]] = None, +) -> OAuthClientProvider: + """Create an OAuth provider for MCP server authentication.""" + + client_metadata_dict = { + "client_name": "Letta MCP Client", + "redirect_uris": [redirect_uri], + "grant_types": ["authorization_code", "refresh_token"], + "response_types": ["code"], + "token_endpoint_auth_method": "client_secret_post", + } + + # Use manager-based storage + storage = DatabaseTokenStorage(session_id, mcp_manager, actor) + + # Extract base URL (remove /mcp endpoint if present) + oauth_server_url = server_url.rstrip("/").removesuffix("/sse").removesuffix("/mcp") + + async def redirect_handler(authorization_url: str) -> None: + """Handle OAuth redirect by storing the authorization URL.""" + logger.info(f"OAuth redirect handler called with URL: {authorization_url}") + session_update = MCPOAuthSessionUpdate(authorization_url=authorization_url) + await mcp_manager.update_oauth_session(session_id, session_update, actor) + logger.info(f"OAuth authorization URL stored: {authorization_url}") + + # Call the callback if provided (e.g., to yield URL to SSE stream) + if url_callback: + url_callback(authorization_url) + + async def callback_handler() -> Tuple[str, Optional[str]]: + """Handle OAuth callback by waiting for authorization code.""" + timeout = 300 # 5 minutes + start_time = time.time() + + logger.info(f"Waiting for authorization code for session {session_id}") + while time.time() - start_time < timeout: + oauth_session = await mcp_manager.get_oauth_session_by_id(session_id, actor) + if oauth_session and oauth_session.authorization_code: + return oauth_session.authorization_code, oauth_session.state + elif oauth_session and oauth_session.status == OAuthSessionStatus.ERROR: + raise Exception("OAuth authorization failed") + await asyncio.sleep(1) + + raise Exception(f"Timeout waiting for OAuth callback after {timeout} seconds") + + return OAuthClientProvider( + server_url=oauth_server_url, + client_metadata=OAuthClientMetadata.model_validate(client_metadata_dict), + storage=storage, + redirect_handler=redirect_handler, + callback_handler=callback_handler, + ) + + +async def cleanup_expired_oauth_sessions(max_age_hours: int = 24) -> None: + """Clean up expired OAuth sessions.""" + cutoff_time = datetime.now() - timedelta(hours=max_age_hours) + + async with db_registry.async_session() as session: + result = await session.execute(select(MCPOAuth).where(MCPOAuth.created_at < cutoff_time)) + expired_sessions = result.scalars().all() + + for oauth_session in expired_sessions: + await oauth_session.hard_delete_async(db_session=session, actor=None) + + if expired_sessions: + logger.info(f"Cleaned up {len(expired_sessions)} expired OAuth sessions") + + +def oauth_stream_event(event: OauthStreamEvent, **kwargs) -> str: + data = {"event": event.value} + data.update(kwargs) + return f"data: {json.dumps(data)}\n\n" + + +def drill_down_exception(exception, depth=0, max_depth=5): + """Recursively drill down into nested exceptions to find the root cause""" + indent = " " * depth + error_details = [] + + error_details.append(f"{indent}Exception at depth {depth}:") + error_details.append(f"{indent} Type: {type(exception).__name__}") + error_details.append(f"{indent} Message: {str(exception)}") + error_details.append(f"{indent} Module: {getattr(type(exception), '__module__', 'unknown')}") + + # Check for exception groups (TaskGroup errors) + if hasattr(exception, "exceptions") and exception.exceptions: + error_details.append(f"{indent} ExceptionGroup with {len(exception.exceptions)} sub-exceptions:") + for i, sub_exc in enumerate(exception.exceptions): + error_details.append(f"{indent} Sub-exception {i}:") + if depth < max_depth: + error_details.extend(drill_down_exception(sub_exc, depth + 1, max_depth)) + + # Check for chained exceptions (__cause__ and __context__) + if hasattr(exception, "__cause__") and exception.__cause__ and depth < max_depth: + error_details.append(f"{indent} Caused by:") + error_details.extend(drill_down_exception(exception.__cause__, depth + 1, max_depth)) + + if hasattr(exception, "__context__") and exception.__context__ and depth < max_depth: + error_details.append(f"{indent} Context:") + error_details.extend(drill_down_exception(exception.__context__, depth + 1, max_depth)) + + # Add traceback info + import traceback + + if hasattr(exception, "__traceback__") and exception.__traceback__: + tb_lines = traceback.format_tb(exception.__traceback__) + error_details.append(f"{indent} Traceback:") + for line in tb_lines[-3:]: # Show last 3 traceback lines + error_details.append(f"{indent} {line.strip()}") + + error_info = "".join(error_details) + return error_info + + +def get_oauth_success_html() -> str: + """Generate HTML for successful OAuth authorization.""" + return """ + + + + Authorization Successful - Letta + + + +
+ +

Authorization Successful

+

You have successfully connected your MCP server.

+
+ You can now close this window. +
+
+ + +""" diff --git a/letta/services/mcp/sse_client.py b/letta/services/mcp/sse_client.py index 91e2515c..950b4ae0 100644 --- a/letta/services/mcp/sse_client.py +++ b/letta/services/mcp/sse_client.py @@ -1,4 +1,7 @@ +from typing import Optional + from mcp import ClientSession +from mcp.client.auth import OAuthClientProvider from mcp.client.sse import sse_client from letta.functions.mcp_client.types import SSEServerConfig @@ -13,6 +16,9 @@ logger = get_logger(__name__) # TODO: Get rid of Async prefix on this class name once we deprecate old sync code class AsyncSSEMCPClient(AsyncBaseMCPClient): + def __init__(self, server_config: SSEServerConfig, oauth_provider: Optional[OAuthClientProvider] = None): + super().__init__(server_config, oauth_provider) + async def _initialize_connection(self, server_config: SSEServerConfig) -> None: headers = {} if server_config.custom_headers: @@ -21,7 +27,12 @@ class AsyncSSEMCPClient(AsyncBaseMCPClient): if server_config.auth_header and server_config.auth_token: headers[server_config.auth_header] = server_config.auth_token - sse_cm = sse_client(url=server_config.server_url, headers=headers if headers else None) + # Use OAuth provider if available, otherwise use regular headers + if self.oauth_provider: + sse_cm = sse_client(url=server_config.server_url, headers=headers if headers else None, auth=self.oauth_provider) + else: + sse_cm = sse_client(url=server_config.server_url, headers=headers if headers else None) + sse_transport = await self.exit_stack.enter_async_context(sse_cm) self.stdio, self.write = sse_transport diff --git a/letta/services/mcp/streamable_http_client.py b/letta/services/mcp/streamable_http_client.py index 63c269ac..baf2f7c6 100644 --- a/letta/services/mcp/streamable_http_client.py +++ b/letta/services/mcp/streamable_http_client.py @@ -1,4 +1,7 @@ +from typing import Optional + from mcp import ClientSession +from mcp.client.auth import OAuthClientProvider from mcp.client.streamable_http import streamablehttp_client from letta.functions.mcp_client.types import BaseServerConfig, StreamableHTTPServerConfig @@ -9,10 +12,12 @@ logger = get_logger(__name__) class AsyncStreamableHTTPMCPClient(AsyncBaseMCPClient): + def __init__(self, server_config: StreamableHTTPServerConfig, oauth_provider: Optional[OAuthClientProvider] = None): + super().__init__(server_config, oauth_provider) + async def _initialize_connection(self, server_config: BaseServerConfig) -> None: if not isinstance(server_config, StreamableHTTPServerConfig): raise ValueError("Expected StreamableHTTPServerConfig") - try: # Prepare headers for authentication headers = {} @@ -23,11 +28,18 @@ class AsyncStreamableHTTPMCPClient(AsyncBaseMCPClient): if server_config.auth_header and server_config.auth_token: headers[server_config.auth_header] = server_config.auth_token - # Use streamablehttp_client context manager with headers if provided - if headers: - streamable_http_cm = streamablehttp_client(server_config.server_url, headers=headers) + # Use OAuth provider if available, otherwise use regular headers + if self.oauth_provider: + streamable_http_cm = streamablehttp_client( + server_config.server_url, headers=headers if headers else None, auth=self.oauth_provider + ) else: - streamable_http_cm = streamablehttp_client(server_config.server_url) + # Use streamablehttp_client context manager with headers if provided + if headers: + streamable_http_cm = streamablehttp_client(server_config.server_url, headers=headers) + else: + streamable_http_cm = streamablehttp_client(server_config.server_url) + read_stream, write_stream, _ = await self.exit_stack.enter_async_context(streamable_http_cm) # Create and enter the ClientSession context manager diff --git a/letta/services/mcp/types.py b/letta/services/mcp/types.py index 2d8b7af6..b5e873cb 100644 --- a/letta/services/mcp/types.py +++ b/letta/services/mcp/types.py @@ -46,3 +46,12 @@ class StdioServerConfig(BaseServerConfig): if self.env is not None: values["env"] = self.env return values + + +class OauthStreamEvent(str, Enum): + CONNECTION_ATTEMPT = "connection_attempt" + SUCCESS = "success" + ERROR = "error" + OAUTH_REQUIRED = "oauth_required" + AUTHORIZATION_URL = "authorization_url" + WAITING_FOR_AUTH = "waiting_for_auth" diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index 77cd2a9a..a075b5d0 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -1,5 +1,8 @@ import json import os +import secrets +import uuid +from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Tuple, Union from sqlalchemy import null @@ -8,8 +11,18 @@ import letta.constants as constants from letta.functions.mcp_client.types import MCPServerType, MCPTool, SSEServerConfig, StdioServerConfig, StreamableHTTPServerConfig from letta.log import get_logger from letta.orm.errors import NoResultFound +from letta.orm.mcp_oauth import MCPOAuth, OAuthSessionStatus from letta.orm.mcp_server import MCPServer as MCPServerModel -from letta.schemas.mcp import MCPServer, UpdateMCPServer, UpdateSSEMCPServer, UpdateStdioMCPServer, UpdateStreamableHTTPMCPServer +from letta.schemas.mcp import ( + MCPOAuthSession, + MCPOAuthSessionCreate, + MCPOAuthSessionUpdate, + MCPServer, + UpdateMCPServer, + UpdateSSEMCPServer, + UpdateStdioMCPServer, + UpdateStreamableHTTPMCPServer, +) from letta.schemas.tool import Tool as PydanticTool from letta.schemas.tool import ToolCreate from letta.schemas.user import User as PydanticUser @@ -38,14 +51,7 @@ class MCPManager: mcp_config = await self.get_mcp_server_by_id_async(mcp_server_id, actor=actor) server_config = mcp_config.to_config() - if mcp_config.server_type == MCPServerType.SSE: - mcp_client = AsyncSSEMCPClient(server_config=server_config) - elif mcp_config.server_type == MCPServerType.STDIO: - mcp_client = AsyncStdioMCPClient(server_config=server_config) - elif mcp_config.server_type == MCPServerType.STREAMABLE_HTTP: - mcp_client = AsyncStreamableHTTPMCPClient(server_config=server_config) - else: - raise ValueError(f"Unsupported MCP server type: {mcp_config.server_type}") + mcp_client = await self.get_mcp_client(server_config, actor) await mcp_client.connect_to_server() # list tools @@ -72,28 +78,20 @@ class MCPManager: # read from config file mcp_config = self.read_mcp_config() if mcp_server_name not in mcp_config: - print("MCP server not found in config.", mcp_config) raise ValueError(f"MCP server {mcp_server_name} not found in config.") server_config = mcp_config[mcp_server_name] - if isinstance(server_config, SSEServerConfig): - # mcp_client = AsyncSSEMCPClient(server_config=server_config) - async with AsyncSSEMCPClient(server_config=server_config) as mcp_client: - result, success = await mcp_client.execute_tool(tool_name, tool_args) - logger.info(f"MCP Result: {result}, Success: {success}") - return result, success - elif isinstance(server_config, StdioServerConfig): - async with AsyncStdioMCPClient(server_config=server_config) as mcp_client: - result, success = await mcp_client.execute_tool(tool_name, tool_args) - logger.info(f"MCP Result: {result}, Success: {success}") - return result, success - elif isinstance(server_config, StreamableHTTPServerConfig): - async with AsyncStreamableHTTPMCPClient(server_config=server_config) as mcp_client: - result, success = await mcp_client.execute_tool(tool_name, tool_args) - logger.info(f"MCP Result: {result}, Success: {success}") - return result, success - else: - raise ValueError(f"Unsupported server config type: {type(server_config)}") + mcp_client = await self.get_mcp_client(server_config, actor) + await mcp_client.connect_to_server() + + # call tool + result, success = await mcp_client.execute_tool(tool_name, tool_args) + logger.info(f"MCP Result: {result}, Success: {success}") + # TODO: change to pydantic tool + + await mcp_client.cleanup() + + return result, success @enforce_types async def add_tool_from_mcp_server(self, mcp_server_name: str, mcp_tool_name: str, actor: PydanticUser) -> PydanticTool: @@ -324,3 +322,246 @@ class MCPManager: logger.error(f"Failed to parse server params for MCP server {server_name} (skipping): {e}") continue return mcp_server_list + + async def get_mcp_client( + self, + server_config: Union[SSEServerConfig, StdioServerConfig, StreamableHTTPServerConfig], + actor: PydanticUser, + oauth_provider: Optional[Any] = None, + ) -> Union[AsyncSSEMCPClient, AsyncStdioMCPClient, AsyncStreamableHTTPMCPClient]: + """ + Helper function to create the appropriate MCP client based on server configuration. + + Args: + server_config: The server configuration object + actor: The user making the request + oauth_provider: Optional OAuth provider for authentication + + Returns: + The appropriate MCP client instance + + Raises: + ValueError: If server config type is not supported + """ + # If no OAuth provider is provided, check if we have stored OAuth credentials + if oauth_provider is None and hasattr(server_config, "server_url"): + oauth_session = await self.get_oauth_session_by_server(server_config.server_url, actor) + if oauth_session and oauth_session.access_token: + # Create OAuth provider from stored credentials + from letta.services.mcp.oauth_utils import create_oauth_provider + + oauth_provider = await create_oauth_provider( + session_id=oauth_session.id, + server_url=oauth_session.server_url, + redirect_uri=oauth_session.redirect_uri, + mcp_manager=self, + actor=actor, + ) + + if server_config.type == MCPServerType.SSE: + server_config = SSEServerConfig(**server_config.model_dump()) + return AsyncSSEMCPClient(server_config=server_config, oauth_provider=oauth_provider) + elif server_config.type == MCPServerType.STDIO: + server_config = StdioServerConfig(**server_config.model_dump()) + return AsyncStdioMCPClient(server_config=server_config, oauth_provider=oauth_provider) + elif server_config.type == MCPServerType.STREAMABLE_HTTP: + server_config = StreamableHTTPServerConfig(**server_config.model_dump()) + return AsyncStreamableHTTPMCPClient(server_config=server_config, oauth_provider=oauth_provider) + else: + raise ValueError(f"Unsupported server config type: {type(server_config)}") + + # OAuth-related methods + @enforce_types + async def create_oauth_session(self, session_create: MCPOAuthSessionCreate, actor: PydanticUser) -> MCPOAuthSession: + """Create a new OAuth session for MCP server authentication.""" + async with db_registry.async_session() as session: + # Create the OAuth session with a unique state + oauth_session = MCPOAuth( + id="mcp-oauth-" + str(uuid.uuid4())[:8], + state=secrets.token_urlsafe(32), + server_url=session_create.server_url, + server_name=session_create.server_name, + user_id=session_create.user_id, + organization_id=session_create.organization_id, + status=OAuthSessionStatus.PENDING, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + oauth_session = await oauth_session.create_async(session, actor=actor) + + # Convert to Pydantic model + return MCPOAuthSession( + id=oauth_session.id, + state=oauth_session.state, + server_url=oauth_session.server_url, + server_name=oauth_session.server_name, + user_id=oauth_session.user_id, + organization_id=oauth_session.organization_id, + status=oauth_session.status, + created_at=oauth_session.created_at, + updated_at=oauth_session.updated_at, + ) + + @enforce_types + async def get_oauth_session_by_id(self, session_id: str, actor: PydanticUser) -> Optional[MCPOAuthSession]: + """Get an OAuth session by its ID.""" + async with db_registry.async_session() as session: + try: + oauth_session = await MCPOAuth.read_async(db_session=session, identifier=session_id, actor=actor) + return MCPOAuthSession( + id=oauth_session.id, + state=oauth_session.state, + server_url=oauth_session.server_url, + server_name=oauth_session.server_name, + user_id=oauth_session.user_id, + organization_id=oauth_session.organization_id, + authorization_url=oauth_session.authorization_url, + authorization_code=oauth_session.authorization_code, + access_token=oauth_session.access_token, + refresh_token=oauth_session.refresh_token, + token_type=oauth_session.token_type, + expires_at=oauth_session.expires_at, + scope=oauth_session.scope, + client_id=oauth_session.client_id, + client_secret=oauth_session.client_secret, + redirect_uri=oauth_session.redirect_uri, + status=oauth_session.status, + created_at=oauth_session.created_at, + updated_at=oauth_session.updated_at, + ) + except NoResultFound: + return None + + @enforce_types + async def get_oauth_session_by_server(self, server_url: str, actor: PydanticUser) -> Optional[MCPOAuthSession]: + """Get the latest OAuth session by server URL, organization, and user.""" + from sqlalchemy import desc, select + + async with db_registry.async_session() as session: + # Query for OAuth session matching organization, user, server URL, and status + # Order by updated_at desc to get the most recent record + result = await session.execute( + select(MCPOAuth) + .where( + MCPOAuth.organization_id == actor.organization_id, + MCPOAuth.user_id == actor.id, + MCPOAuth.server_url == server_url, + MCPOAuth.status == OAuthSessionStatus.AUTHORIZED, + ) + .order_by(desc(MCPOAuth.updated_at)) + .limit(1) + ) + oauth_session = result.scalar_one_or_none() + + if not oauth_session: + return None + + return MCPOAuthSession( + id=oauth_session.id, + state=oauth_session.state, + server_url=oauth_session.server_url, + server_name=oauth_session.server_name, + user_id=oauth_session.user_id, + organization_id=oauth_session.organization_id, + authorization_url=oauth_session.authorization_url, + authorization_code=oauth_session.authorization_code, + access_token=oauth_session.access_token, + refresh_token=oauth_session.refresh_token, + token_type=oauth_session.token_type, + expires_at=oauth_session.expires_at, + scope=oauth_session.scope, + client_id=oauth_session.client_id, + client_secret=oauth_session.client_secret, + redirect_uri=oauth_session.redirect_uri, + status=oauth_session.status, + created_at=oauth_session.created_at, + updated_at=oauth_session.updated_at, + ) + + @enforce_types + async def update_oauth_session(self, session_id: str, session_update: MCPOAuthSessionUpdate, actor: PydanticUser) -> MCPOAuthSession: + """Update an existing OAuth session.""" + async with db_registry.async_session() as session: + oauth_session = await MCPOAuth.read_async(db_session=session, identifier=session_id, actor=actor) + + # Update fields that are provided + if session_update.authorization_url is not None: + oauth_session.authorization_url = session_update.authorization_url + if session_update.authorization_code is not None: + oauth_session.authorization_code = session_update.authorization_code + if session_update.access_token is not None: + oauth_session.access_token = session_update.access_token + if session_update.refresh_token is not None: + oauth_session.refresh_token = session_update.refresh_token + if session_update.token_type is not None: + oauth_session.token_type = session_update.token_type + if session_update.expires_at is not None: + oauth_session.expires_at = session_update.expires_at + if session_update.scope is not None: + oauth_session.scope = session_update.scope + if session_update.client_id is not None: + oauth_session.client_id = session_update.client_id + if session_update.client_secret is not None: + oauth_session.client_secret = session_update.client_secret + if session_update.redirect_uri is not None: + oauth_session.redirect_uri = session_update.redirect_uri + if session_update.status is not None: + oauth_session.status = session_update.status + + # Always update the updated_at timestamp + oauth_session.updated_at = datetime.now() + + oauth_session = await oauth_session.update_async(db_session=session, actor=actor) + + return MCPOAuthSession( + id=oauth_session.id, + state=oauth_session.state, + server_url=oauth_session.server_url, + server_name=oauth_session.server_name, + user_id=oauth_session.user_id, + organization_id=oauth_session.organization_id, + authorization_url=oauth_session.authorization_url, + authorization_code=oauth_session.authorization_code, + access_token=oauth_session.access_token, + refresh_token=oauth_session.refresh_token, + token_type=oauth_session.token_type, + expires_at=oauth_session.expires_at, + scope=oauth_session.scope, + client_id=oauth_session.client_id, + client_secret=oauth_session.client_secret, + redirect_uri=oauth_session.redirect_uri, + status=oauth_session.status, + created_at=oauth_session.created_at, + updated_at=oauth_session.updated_at, + ) + + @enforce_types + async def delete_oauth_session(self, session_id: str, actor: PydanticUser) -> None: + """Delete an OAuth session.""" + async with db_registry.async_session() as session: + try: + oauth_session = await MCPOAuth.read_async(db_session=session, identifier=session_id, actor=actor) + await oauth_session.hard_delete_async(db_session=session, actor=actor) + except NoResultFound: + raise ValueError(f"OAuth session with id {session_id} not found.") + + @enforce_types + async def cleanup_expired_oauth_sessions(self, max_age_hours: int = 24) -> int: + """Clean up expired OAuth sessions and return the count of deleted sessions.""" + cutoff_time = datetime.now() - timedelta(hours=max_age_hours) + + async with db_registry.async_session() as session: + from sqlalchemy import select + + # Find expired sessions + result = await session.execute(select(MCPOAuth).where(MCPOAuth.created_at < cutoff_time)) + expired_sessions = result.scalars().all() + + # Delete expired sessions using async ORM method + for oauth_session in expired_sessions: + await oauth_session.hard_delete_async(db_session=session, actor=None) + + if expired_sessions: + logger.info(f"Cleaned up {len(expired_sessions)} expired OAuth sessions") + + return len(expired_sessions) diff --git a/mcp_test.py b/mcp_test.py new file mode 100644 index 00000000..507d5c7c --- /dev/null +++ b/mcp_test.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python3 +""" +Simple MCP client example with OAuth authentication support. + +This client connects to an MCP server using streamable HTTP transport with OAuth. + +""" + +import asyncio +import os +import threading +import time +import webbrowser +from datetime import timedelta +from http.server import BaseHTTPRequestHandler, HTTPServer +from typing import Any +from urllib.parse import parse_qs, urlparse + +from mcp.client.auth import OAuthClientProvider, TokenStorage +from mcp.client.session import ClientSession +from mcp.client.sse import sse_client +from mcp.client.streamable_http import streamablehttp_client +from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken + + +class InMemoryTokenStorage(TokenStorage): + """Simple in-memory token storage implementation.""" + + def __init__(self): + self._tokens: OAuthToken | None = None + self._client_info: OAuthClientInformationFull | None = None + + async def get_tokens(self) -> OAuthToken | None: + return self._tokens + + async def set_tokens(self, tokens: OAuthToken) -> None: + self._tokens = tokens + + async def get_client_info(self) -> OAuthClientInformationFull | None: + return self._client_info + + async def set_client_info(self, client_info: OAuthClientInformationFull) -> None: + self._client_info = client_info + + +class CallbackHandler(BaseHTTPRequestHandler): + """Simple HTTP handler to capture OAuth callback.""" + + def __init__(self, request, client_address, server, callback_data): + """Initialize with callback data storage.""" + self.callback_data = callback_data + super().__init__(request, client_address, server) + + def do_GET(self): + """Handle GET request from OAuth redirect.""" + parsed = urlparse(self.path) + query_params = parse_qs(parsed.query) + + if "code" in query_params: + self.callback_data["authorization_code"] = query_params["code"][0] + self.callback_data["state"] = query_params.get("state", [None])[0] + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write( + b""" + + +

Authorization Successful!

+

You can close this window and return to the terminal.

+ + + + """ + ) + elif "error" in query_params: + self.callback_data["error"] = query_params["error"][0] + self.send_response(400) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write( + f""" + + +

Authorization Failed

+

Error: {query_params['error'][0]}

+

You can close this window and return to the terminal.

+ + + """.encode() + ) + else: + self.send_response(404) + self.end_headers() + + def log_message(self, format, *args): + """Suppress default logging.""" + + +class CallbackServer: + """Simple server to handle OAuth callbacks.""" + + def __init__(self, port=3000): + self.port = port + self.server = None + self.thread = None + self.callback_data = {"authorization_code": None, "state": None, "error": None} + + def _create_handler_with_data(self): + """Create a handler class with access to callback data.""" + callback_data = self.callback_data + + class DataCallbackHandler(CallbackHandler): + def __init__(self, request, client_address, server): + super().__init__(request, client_address, server, callback_data) + + return DataCallbackHandler + + def start(self): + """Start the callback server in a background thread.""" + handler_class = self._create_handler_with_data() + self.server = HTTPServer(("localhost", self.port), handler_class) + self.thread = threading.Thread(target=self.server.serve_forever, daemon=True) + self.thread.start() + print(f"🖥️ Started callback server on http://localhost:{self.port}") + + def stop(self): + """Stop the callback server.""" + if self.server: + self.server.shutdown() + self.server.server_close() + if self.thread: + self.thread.join(timeout=1) + + def wait_for_callback(self, timeout=300): + """Wait for OAuth callback with timeout.""" + start_time = time.time() + while time.time() - start_time < timeout: + if self.callback_data["authorization_code"]: + return self.callback_data["authorization_code"] + elif self.callback_data["error"]: + raise Exception(f"OAuth error: {self.callback_data['error']}") + time.sleep(0.1) + raise Exception("Timeout waiting for OAuth callback") + + def get_state(self): + """Get the received state parameter.""" + return self.callback_data["state"] + + +class SimpleAuthClient: + """Simple MCP client with auth support.""" + + def __init__(self, server_url: str, transport_type: str = "streamable_http"): + self.server_url = server_url + self.transport_type = transport_type + self.session: ClientSession | None = None + + async def connect(self): + """Connect to the MCP server.""" + print(f"🔗 Attempting to connect to {self.server_url}...") + + try: + callback_server = CallbackServer(port=3030) + callback_server.start() + + async def callback_handler() -> tuple[str, str | None]: + """Wait for OAuth callback and return auth code and state.""" + print("⏳ Waiting for authorization callback...") + try: + auth_code = callback_server.wait_for_callback(timeout=300) + return auth_code, callback_server.get_state() + finally: + callback_server.stop() + + client_metadata_dict = { + "client_name": "Simple Auth Client", + "redirect_uris": ["http://localhost:3030/callback"], + "grant_types": ["authorization_code", "refresh_token"], + "response_types": ["code"], + "token_endpoint_auth_method": "client_secret_post", + } + + async def _default_redirect_handler(authorization_url: str) -> None: + """Default redirect handler that opens the URL in a browser.""" + print(f"Opening browser for authorization: {authorization_url}") + webbrowser.open(authorization_url) + + # Create OAuth authentication handler using the new interface + oauth_auth = OAuthClientProvider( + server_url=self.server_url.replace("/mcp", ""), + client_metadata=OAuthClientMetadata.model_validate(client_metadata_dict), + storage=InMemoryTokenStorage(), + redirect_handler=_default_redirect_handler, + callback_handler=callback_handler, + ) + + # Create transport with auth handler based on transport type + if self.transport_type == "sse": + print("📡 Opening SSE transport connection with auth...") + async with sse_client( + url=self.server_url, + auth=oauth_auth, + timeout=60, + ) as (read_stream, write_stream): + await self._run_session(read_stream, write_stream, None) + else: + print("📡 Opening StreamableHTTP transport connection with auth...") + async with streamablehttp_client( + url=self.server_url, + auth=oauth_auth, + timeout=timedelta(seconds=60), + ) as (read_stream, write_stream, get_session_id): + await self._run_session(read_stream, write_stream, get_session_id) + + except Exception as e: + print(f"❌ Failed to connect: {e}") + import traceback + + traceback.print_exc() + + async def _run_session(self, read_stream, write_stream, get_session_id): + """Run the MCP session with the given streams.""" + print("🤝 Initializing MCP session...") + async with ClientSession(read_stream, write_stream) as session: + self.session = session + print("⚡ Starting session initialization...") + await session.initialize() + print("✨ Session initialization complete!") + + print(f"\n✅ Connected to MCP server at {self.server_url}") + if get_session_id: + session_id = get_session_id() + if session_id: + print(f"Session ID: {session_id}") + + # Run interactive loop + await self.interactive_loop() + + async def list_tools(self): + """List available tools from the server.""" + if not self.session: + print("❌ Not connected to server") + return + + try: + result = await self.session.list_tools() + if hasattr(result, "tools") and result.tools: + print("\n📋 Available tools:") + for i, tool in enumerate(result.tools, 1): + print(f"{i}. {tool.name}") + if tool.description: + print(f" Description: {tool.description}") + print() + else: + print("No tools available") + except Exception as e: + print(f"❌ Failed to list tools: {e}") + + async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None = None): + """Call a specific tool.""" + if not self.session: + print("❌ Not connected to server") + return + + try: + result = await self.session.call_tool(tool_name, arguments or {}) + print(f"\n🔧 Tool '{tool_name}' result:") + if hasattr(result, "content"): + for content in result.content: + if content.type == "text": + print(content.text) + else: + print(content) + else: + print(result) + except Exception as e: + print(f"❌ Failed to call tool '{tool_name}': {e}") + + async def interactive_loop(self): + """Run interactive command loop.""" + print("\n🎯 Interactive MCP Client") + print("Commands:") + print(" list - List available tools") + print(" call [args] - Call a tool") + print(" quit - Exit the client") + print() + + while True: + try: + command = input("mcp> ").strip() + + if not command: + continue + + if command == "quit": + break + + elif command == "list": + await self.list_tools() + + elif command.startswith("call "): + parts = command.split(maxsplit=2) + tool_name = parts[1] if len(parts) > 1 else "" + + if not tool_name: + print("❌ Please specify a tool name") + continue + + # Parse arguments (simple JSON-like format) + arguments = {} + if len(parts) > 2: + import json + + try: + arguments = json.loads(parts[2]) + except json.JSONDecodeError: + print("❌ Invalid arguments format (expected JSON)") + continue + + await self.call_tool(tool_name, arguments) + + else: + print("❌ Unknown command. Try 'list', 'call ', or 'quit'") + + except KeyboardInterrupt: + print("\n\n👋 Goodbye!") + break + except EOFError: + break + + +async def main(): + """Main entry point.""" + # Default server URL - can be overridden with environment variable + # Most MCP streamable HTTP servers use /mcp as the endpoint + server_url = os.getenv("MCP_SERVER_PORT", 8000) + transport_type = os.getenv("MCP_TRANSPORT_TYPE", "streamable_http") + server_url = f"http://localhost:{server_url}/mcp" if transport_type == "streamable_http" else f"http://localhost:{server_url}/sse" + + print("🚀 Simple MCP Auth Client") + print(f"Connecting to: {server_url}") + print(f"Transport type: {transport_type}") + + # Start connection flow - OAuth will be handled automatically + client = SimpleAuthClient(server_url, transport_type) + await client.connect() + + +def cli(): + """CLI entry point for uv script.""" + asyncio.run(main()) + + +if __name__ == "__main__": + cli() From b2743e2454110131e43c01d360113028a25a3e4d Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Thu, 24 Jul 2025 20:51:39 -0700 Subject: [PATCH 13/24] feat: agent tags reverse index --- ...add_index_for_agent_tags_reversed_order.py | 37 +++++++++++++++++++ letta/orm/agents_tags.py | 1 + letta/settings.py | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 alembic/versions/f55542f37641_add_index_for_agent_tags_reversed_order.py diff --git a/alembic/versions/f55542f37641_add_index_for_agent_tags_reversed_order.py b/alembic/versions/f55542f37641_add_index_for_agent_tags_reversed_order.py new file mode 100644 index 00000000..5a0d13d2 --- /dev/null +++ b/alembic/versions/f55542f37641_add_index_for_agent_tags_reversed_order.py @@ -0,0 +1,37 @@ +"""add index for agent_tags reversed order + +Revision ID: f55542f37641 +Revises: ddecfe4902bc +Create Date: 2025-07-24 18:00:30.773048 + +""" + +from typing import Sequence, Union + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "f55542f37641" +down_revision: Union[str, None] = "f5d26b0526e8" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Note some issues at least with pg8000 with concurrent index creation + # with op.get_context().autocommit_block(): + # op.create_index( + # op.f('ix_agent_tags_tag_agent_id'), + # "agents_tags", + # ['tag', 'agent_id'], + # unique=False, + # postgresql_concurrently=True, + # ) + op.create_index("ix_agents_tags_tag_agent_id", "agents_tags", ["tag", "agent_id"], unique=False) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index("ix_agents_tags_tag_agent_id", table_name="agents_tags") + # ### end Alembic commands ### diff --git a/letta/orm/agents_tags.py b/letta/orm/agents_tags.py index 438910a3..d7177083 100644 --- a/letta/orm/agents_tags.py +++ b/letta/orm/agents_tags.py @@ -9,6 +9,7 @@ class AgentsTags(Base): __table_args__ = ( UniqueConstraint("agent_id", "tag", name="unique_agent_tag"), Index("ix_agents_tags_agent_id_tag", "agent_id", "tag"), + Index("ix_agents_tags_tag_agent_id", "tag", "agent_id"), ) # # agent generates its own id diff --git a/letta/settings.py b/letta/settings.py index f7537797..3f91b27a 100644 --- a/letta/settings.py +++ b/letta/settings.py @@ -242,7 +242,7 @@ class Settings(BaseSettings): uvicorn_reload: bool = False uvicorn_timeout_keep_alive: int = 5 - use_uvloop: bool = Field(default=True, description="Enable uvloop as asyncio event loop.") + use_uvloop: bool = Field(default=False, description="Enable uvloop as asyncio event loop.") use_granian: bool = Field(default=False, description="Use Granian for workers") sqlalchemy_tracing: bool = False From dbb899644255d82536992a724baf715d93028cb3 Mon Sep 17 00:00:00 2001 From: Eric Ly <111820150+lyeric2022@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:00:51 -0700 Subject: [PATCH 14/24] revert: access_key_id to access_key (#3556) Co-authored-by: Eric Ly --- letta/services/provider_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letta/services/provider_manager.py b/letta/services/provider_manager.py index 675ff763..46fe615e 100644 --- a/letta/services/provider_manager.py +++ b/letta/services/provider_manager.py @@ -213,12 +213,12 @@ class ProviderManager: provider_type=provider_check.provider_type, api_key=provider_check.api_key, provider_category=ProviderCategory.byok, - access_id_key=provider_check.access_id_key, # This contains the access key ID for Bedrock + access_key=provider_check.access_key, # This contains the access key ID for Bedrock region=provider_check.region, ).cast_to_subtype() # TODO: add more string sanity checks here before we hit actual endpoints if not provider.api_key: - raise ValueError("API key is required") + raise ValueError("API key is required!") await provider.check_api_key() From 82fef362bef7da926bdbbccbd4b63dadbdaf62da Mon Sep 17 00:00:00 2001 From: jnjpng Date: Fri, 25 Jul 2025 11:13:05 -0700 Subject: [PATCH 15/24] fix: `test_agent_serialization_v2.py` and use bulk fetch when fetching mcp servers Co-authored-by: Jin Peng --- letta/services/agent_serialization_manager.py | 16 ++++++---------- letta/services/mcp_manager.py | 19 +++++++++++++++++-- tests/test_agent_serialization_v2.py | 1 + tests/test_managers.py | 4 ++-- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/letta/services/agent_serialization_manager.py b/letta/services/agent_serialization_manager.py index 166d1608..3cb1ca02 100644 --- a/letta/services/agent_serialization_manager.py +++ b/letta/services/agent_serialization_manager.py @@ -1,6 +1,7 @@ from datetime import datetime, timezone from typing import Dict, List +from letta.constants import MCP_TOOL_TAG_NAME_PREFIX from letta.errors import AgentFileExportError, AgentFileImportError from letta.helpers.pinecone_utils import should_use_pinecone from letta.log import get_logger @@ -262,8 +263,6 @@ class AgentSerializationManager: async def _extract_unique_mcp_servers(self, tools: List, actor: User) -> List: """Extract unique MCP servers from tools based on metadata, using server_id if available, otherwise falling back to server_name.""" - from letta.constants import MCP_TOOL_TAG_NAME_PREFIX - mcp_server_ids = set() mcp_server_names = set() for tool in tools: @@ -280,14 +279,11 @@ class AgentSerializationManager: mcp_servers = [] fetched_server_ids = set() if mcp_server_ids: - for server_id in mcp_server_ids: - try: - mcp_server = await self.mcp_manager.get_mcp_server_by_id_async(server_id, actor) - if mcp_server: - mcp_servers.append(mcp_server) - fetched_server_ids.add(server_id) - except Exception as e: - logger.warning(f"Failed to fetch MCP server {server_id}: {e}") + try: + mcp_servers = await self.mcp_manager.get_mcp_servers_by_ids(list(mcp_server_ids), actor) + fetched_server_ids.update([mcp_server.id for mcp_server in mcp_servers]) + except Exception as e: + logger.warning(f"Failed to fetch MCP servers by IDs {mcp_server_ids}: {e}") # Fetch MCP servers by name if not already fetched by ID if mcp_server_names: diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index a075b5d0..02d37a85 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -5,6 +5,7 @@ import uuid from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Tuple, Union +from fastapi import HTTPException from sqlalchemy import null import letta.constants as constants @@ -199,7 +200,14 @@ class MCPManager: """Update an MCP server by its name.""" mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor) if not mcp_server_id: - raise ValueError(f"MCP server {mcp_server_name} not found") + raise HTTPException( + status_code=404, + detail={ + "code": "MCPServerNotFoundError", + "message": f"MCP server {mcp_server_name} not found", + "mcp_server_name": mcp_server_name, + }, + ) return await self.update_mcp_server_by_id(mcp_server_id, mcp_server_update, actor) @enforce_types @@ -240,7 +248,14 @@ class MCPManager: mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor) mcp_server = await MCPServerModel.read_async(db_session=session, identifier=mcp_server_id, actor=actor) if not mcp_server: - raise ValueError(f"MCP server {mcp_server_name} not found") + raise HTTPException( + status_code=404, # Not Found + detail={ + "code": "MCPServerNotFoundError", + "message": f"MCP server {mcp_server_name} not found", + "mcp_server_name": mcp_server_name, + }, + ) return mcp_server.to_pydantic() # @enforce_types diff --git a/tests/test_agent_serialization_v2.py b/tests/test_agent_serialization_v2.py index 10f5480c..dd0e3b0d 100644 --- a/tests/test_agent_serialization_v2.py +++ b/tests/test_agent_serialization_v2.py @@ -1092,6 +1092,7 @@ class TestAgentFileImport: files=[], sources=[], tools=[], + mcp_servers=[], ) with pytest.raises(AgentFileImportError): diff --git a/tests/test_managers.py b/tests/test_managers.py index 395f3ebc..4b4b0c5f 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -8579,8 +8579,8 @@ async def test_get_mcp_servers_by_ids(server, default_user, event_loop): }, { "name": "test_server_3", - "config": SSEServerConfig(server_name="test_server_3", server_url="https://test3.example.com/sse"), - "type": MCPServerType.SSE, + "config": SSEServerConfig(server_name="test_server_3", server_url="https://test3.example.com/mcp"), + "type": MCPServerType.STREAMABLE_HTTP, }, ] From 70306bdb06ad0c6392d953f0199806aa87ac99d8 Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Fri, 25 Jul 2025 13:06:12 -0700 Subject: [PATCH 16/24] feat: support for project_id and backfills --- ..._support_for_project_id_for_blocks_and_.py | 69 +++++++++++++++++++ letta/orm/agent.py | 5 +- letta/orm/block.py | 4 +- letta/orm/group.py | 4 +- letta/orm/identity.py | 7 +- letta/orm/step.py | 6 +- letta/schemas/block.py | 3 + letta/schemas/group.py | 3 + letta/server/rest_api/routers/v1/blocks.py | 2 + letta/server/rest_api/routers/v1/groups.py | 2 +- letta/server/rest_api/routers/v1/tools.py | 2 +- letta/services/block_manager.py | 4 ++ tests/test_client.py | 5 +- 13 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py diff --git a/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py b/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py new file mode 100644 index 00000000..a2c0e062 --- /dev/null +++ b/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py @@ -0,0 +1,69 @@ +"""support for project_id for blocks and groups + +Revision ID: 06fbbf65d4f1 +Revises: f55542f37641 +Create Date: 2025-07-21 15:07:32.133538 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from sqlalchemy import text + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "06fbbf65d4f1" +down_revision: Union[str, None] = "f55542f37641" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("block", sa.Column("project_id", sa.String(), nullable=True)) + op.add_column("groups", sa.Column("project_id", sa.String(), nullable=True)) + + # Backfill project_id for blocks table + # Since all agents for a block have the same project_id, we can just grab the first one + op.execute( + text( + """ + UPDATE block + SET project_id = ( + SELECT a.project_id + FROM blocks_agents ba + JOIN agents a ON ba.agent_id = a.id + WHERE ba.block_id = block.id + AND a.project_id IS NOT NULL + LIMIT 1 + ) + """ + ) + ) + + # Backfill project_id for groups table + op.execute( + text( + """ + UPDATE groups + SET project_id = ( + SELECT a.project_id + FROM groups_agents ga + JOIN agents a ON ga.agent_id = a.id + WHERE ga.group_id = groups.id + AND a.project_id IS NOT NULL + LIMIT 1 + ) + """ + ) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("groups", "project_id") + op.drop_column("block", "project_id") + # ### end Alembic commands ### diff --git a/letta/orm/agent.py b/letta/orm/agent.py index bc2879d8..a0128f87 100644 --- a/letta/orm/agent.py +++ b/letta/orm/agent.py @@ -10,7 +10,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from letta.orm.block import Block from letta.orm.custom_columns import EmbeddingConfigColumn, LLMConfigColumn, ResponseFormatColumn, ToolRulesColumn from letta.orm.identity import Identity -from letta.orm.mixins import OrganizationMixin +from letta.orm.mixins import OrganizationMixin, ProjectMixin from letta.orm.organization import Organization from letta.orm.sqlalchemy_base import SqlalchemyBase from letta.schemas.agent import AgentState as PydanticAgentState @@ -31,7 +31,7 @@ if TYPE_CHECKING: from letta.orm.tool import Tool -class Agent(SqlalchemyBase, OrganizationMixin, AsyncAttrs): +class Agent(SqlalchemyBase, OrganizationMixin, ProjectMixin, AsyncAttrs): __tablename__ = "agents" __pydantic_model__ = PydanticAgentState __table_args__ = (Index("ix_agents_created_at", "created_at", "id"),) @@ -67,7 +67,6 @@ class Agent(SqlalchemyBase, OrganizationMixin, AsyncAttrs): embedding_config: Mapped[Optional[EmbeddingConfig]] = mapped_column( EmbeddingConfigColumn, doc="the embedding configuration object for this agent." ) - project_id: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The id of the project the agent belongs to.") template_id: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The id of the template the agent belongs to.") base_template_id: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The base template id of the agent.") diff --git a/letta/orm/block.py b/letta/orm/block.py index bbbb6dfc..a31b5a87 100644 --- a/letta/orm/block.py +++ b/letta/orm/block.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import Mapped, attributes, declared_attr, mapped_column, rel from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT from letta.orm.block_history import BlockHistory from letta.orm.blocks_agents import BlocksAgents -from letta.orm.mixins import OrganizationMixin +from letta.orm.mixins import OrganizationMixin, ProjectMixin from letta.orm.sqlalchemy_base import SqlalchemyBase from letta.schemas.block import Block as PydanticBlock from letta.schemas.block import Human, Persona @@ -16,7 +16,7 @@ if TYPE_CHECKING: from letta.orm.identity import Identity -class Block(OrganizationMixin, SqlalchemyBase): +class Block(OrganizationMixin, SqlalchemyBase, ProjectMixin): """Blocks are sections of the LLM context, representing a specific part of the total Memory""" __tablename__ = "block" diff --git a/letta/orm/group.py b/letta/orm/group.py index 489e563f..fe2cfe7e 100644 --- a/letta/orm/group.py +++ b/letta/orm/group.py @@ -4,12 +4,12 @@ from typing import List, Optional from sqlalchemy import JSON, ForeignKey, String from sqlalchemy.orm import Mapped, mapped_column, relationship -from letta.orm.mixins import OrganizationMixin +from letta.orm.mixins import OrganizationMixin, ProjectMixin from letta.orm.sqlalchemy_base import SqlalchemyBase from letta.schemas.group import Group as PydanticGroup -class Group(SqlalchemyBase, OrganizationMixin): +class Group(SqlalchemyBase, OrganizationMixin, ProjectMixin): __tablename__ = "groups" __pydantic_model__ = PydanticGroup diff --git a/letta/orm/identity.py b/letta/orm/identity.py index ac83e952..0d4f13ca 100644 --- a/letta/orm/identity.py +++ b/letta/orm/identity.py @@ -1,17 +1,17 @@ import uuid -from typing import List, Optional +from typing import List from sqlalchemy import String, UniqueConstraint from sqlalchemy.dialects.postgresql import JSON from sqlalchemy.orm import Mapped, mapped_column, relationship -from letta.orm.mixins import OrganizationMixin +from letta.orm.mixins import OrganizationMixin, ProjectMixin from letta.orm.sqlalchemy_base import SqlalchemyBase from letta.schemas.identity import Identity as PydanticIdentity from letta.schemas.identity import IdentityProperty -class Identity(SqlalchemyBase, OrganizationMixin): +class Identity(SqlalchemyBase, OrganizationMixin, ProjectMixin): """Identity ORM class""" __tablename__ = "identities" @@ -32,7 +32,6 @@ class Identity(SqlalchemyBase, OrganizationMixin): identifier_key: Mapped[str] = mapped_column(nullable=False, doc="External, user-generated identifier key of the identity.") name: Mapped[str] = mapped_column(nullable=False, doc="The name of the identity.") identity_type: Mapped[str] = mapped_column(nullable=False, doc="The type of the identity.") - project_id: Mapped[Optional[str]] = mapped_column(nullable=True, doc="The project id of the identity.") properties: Mapped[List["IdentityProperty"]] = mapped_column( JSON, nullable=False, default=list, doc="List of properties associated with the identity" ) diff --git a/letta/orm/step.py b/letta/orm/step.py index 05da631c..e35aa135 100644 --- a/letta/orm/step.py +++ b/letta/orm/step.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional from sqlalchemy import JSON, ForeignKey, String from sqlalchemy.orm import Mapped, mapped_column, relationship +from letta.orm.mixins import ProjectMixin from letta.orm.sqlalchemy_base import SqlalchemyBase from letta.schemas.letta_stop_reason import StopReasonType from letta.schemas.step import Step as PydanticStep @@ -13,7 +14,7 @@ if TYPE_CHECKING: from letta.orm.provider import Provider -class Step(SqlalchemyBase): +class Step(SqlalchemyBase, ProjectMixin): """Tracks all metadata for agent step.""" __tablename__ = "steps" @@ -53,9 +54,6 @@ class Step(SqlalchemyBase): feedback: Mapped[Optional[str]] = mapped_column( None, nullable=True, doc="The feedback for this step. Must be either 'positive' or 'negative'." ) - project_id: Mapped[Optional[str]] = mapped_column( - None, nullable=True, doc="The project that the agent that executed this step belongs to (cloud only)." - ) # Relationships (foreign keys) organization: Mapped[Optional["Organization"]] = relationship("Organization") diff --git a/letta/schemas/block.py b/letta/schemas/block.py index ea30da77..05604575 100644 --- a/letta/schemas/block.py +++ b/letta/schemas/block.py @@ -19,6 +19,7 @@ class BaseBlock(LettaBase, validate_assignment=True): value: str = Field(..., description="Value of the block.") limit: int = Field(CORE_MEMORY_BLOCK_CHAR_LIMIT, description="Character limit of the block.") + project_id: Optional[str] = Field(None, description="The associated project id.") # template data (optional) template_name: Optional[str] = Field(None, description="Name of the block if it is a template.", alias="name") is_template: bool = Field(False, description="Whether the block is a template (e.g. saved human/persona options).") @@ -112,6 +113,7 @@ class BlockUpdate(BaseBlock): limit: Optional[int] = Field(None, description="Character limit of the block.") value: Optional[str] = Field(None, description="Value of the block.") + project_id: Optional[str] = Field(None, description="The associated project id.") class Config: extra = "ignore" # Ignores extra fields @@ -124,6 +126,7 @@ class CreateBlock(BaseBlock): limit: int = Field(CORE_MEMORY_BLOCK_CHAR_LIMIT, description="Character limit of the block.") value: str = Field(..., description="Value of the block.") + project_id: Optional[str] = Field(None, description="The associated project id.") # block templates is_template: bool = False template_name: Optional[str] = Field(None, description="Name of the block if it is a template.", alias="name") diff --git a/letta/schemas/group.py b/letta/schemas/group.py index de40ba5d..fdbfff6d 100644 --- a/letta/schemas/group.py +++ b/letta/schemas/group.py @@ -24,6 +24,7 @@ class Group(GroupBase): manager_type: ManagerType = Field(..., description="") agent_ids: List[str] = Field(..., description="") description: str = Field(..., description="") + project_id: Optional[str] = Field(None, description="The associated project id.") shared_block_ids: List[str] = Field([], description="") # Pattern fields manager_agent_id: Optional[str] = Field(None, description="") @@ -138,6 +139,7 @@ class GroupCreate(BaseModel): agent_ids: List[str] = Field(..., description="") description: str = Field(..., description="") manager_config: ManagerConfigUnion = Field(RoundRobinManager(), description="") + project_id: Optional[str] = Field(None, description="The associated project id.") shared_block_ids: List[str] = Field([], description="") @@ -145,4 +147,5 @@ class GroupUpdate(BaseModel): agent_ids: Optional[List[str]] = Field(None, description="") description: Optional[str] = Field(None, description="") manager_config: Optional[ManagerConfigUpdateUnion] = Field(None, description="") + project_id: Optional[str] = Field(None, description="The associated project id.") shared_block_ids: Optional[List[str]] = Field(None, description="") diff --git a/letta/server/rest_api/routers/v1/blocks.py b/letta/server/rest_api/routers/v1/blocks.py index 2ccdc6f5..0320b832 100644 --- a/letta/server/rest_api/routers/v1/blocks.py +++ b/letta/server/rest_api/routers/v1/blocks.py @@ -22,6 +22,7 @@ async def list_blocks( name: Optional[str] = Query(None, description="Name of the block"), identity_id: Optional[str] = Query(None, description="Search agents by identifier id"), identifier_keys: Optional[List[str]] = Query(None, description="Search agents by identifier keys"), + project_id: Optional[str] = Query(None, description="Search blocks by project id"), limit: Optional[int] = Query(50, description="Number of blocks to return"), server: SyncServer = Depends(get_letta_server), actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present @@ -34,6 +35,7 @@ async def list_blocks( template_name=name, identity_id=identity_id, identifier_keys=identifier_keys, + project_id=project_id, limit=limit, ) diff --git a/letta/server/rest_api/routers/v1/groups.py b/letta/server/rest_api/routers/v1/groups.py index bbb51157..14f95115 100644 --- a/letta/server/rest_api/routers/v1/groups.py +++ b/letta/server/rest_api/routers/v1/groups.py @@ -31,12 +31,12 @@ def list_groups( """ actor = server.user_manager.get_user_or_default(user_id=actor_id) return server.group_manager.list_groups( + actor=actor, project_id=project_id, manager_type=manager_type, before=before, after=after, limit=limit, - actor=actor, ) diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index 354afdc2..1165ebcd 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -703,7 +703,7 @@ async def connect_mcp_server( """ async def oauth_stream_generator( - request: Union[StdioServerConfig, SSEServerConfig, StreamableHTTPServerConfig] + request: Union[StdioServerConfig, SSEServerConfig, StreamableHTTPServerConfig], ) -> AsyncGenerator[str, None]: client = None oauth_provider = None diff --git a/letta/services/block_manager.py b/letta/services/block_manager.py index d8682bd8..c0799026 100644 --- a/letta/services/block_manager.py +++ b/letta/services/block_manager.py @@ -178,6 +178,7 @@ class BlockManager: template_name: Optional[str] = None, identity_id: Optional[str] = None, identifier_keys: Optional[List[str]] = None, + project_id: Optional[str] = None, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 50, @@ -210,6 +211,9 @@ class BlockManager: if template_name: query = query.where(BlockModel.template_name == template_name) + if project_id: + query = query.where(BlockModel.project_id == project_id) + needs_distinct = False if identifier_keys: diff --git a/tests/test_client.py b/tests/test_client.py index 8f6c2a23..77507b0b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -710,7 +710,7 @@ def test_attach_sleeptime_block(client: Letta): sleeptime_id = [id for id in agent_ids if id != agent.id][0] # attach a new block - block = client.blocks.create(label="test", value="test") + block = client.blocks.create(label="test", value="test") # , project_id="test") client.agents.blocks.attach(agent_id=agent.id, block_id=block.id) # verify block is attached to both agents @@ -720,5 +720,8 @@ def test_attach_sleeptime_block(client: Letta): blocks = client.agents.blocks.list(agent_id=sleeptime_id) assert block.id in [b.id for b in blocks] + # blocks = client.blocks.list(project_id="test") + # assert block.id in [b.id for b in blocks] + # cleanup client.agents.delete(agent.id) From efbe9e228f44a1eaf7bfbfb10156e7267f47950b Mon Sep 17 00:00:00 2001 From: jnjpng Date: Fri, 25 Jul 2025 13:07:05 -0700 Subject: [PATCH 17/24] fix: catch and log mcp tool list exceptions Co-authored-by: Jin Peng --- letta/services/mcp_manager.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index 02d37a85..e0361822 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -48,20 +48,21 @@ class MCPManager: @enforce_types async def list_mcp_server_tools(self, mcp_server_name: str, actor: PydanticUser) -> List[MCPTool]: """Get a list of all tools for a specific MCP server.""" - mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor=actor) - mcp_config = await self.get_mcp_server_by_id_async(mcp_server_id, actor=actor) - server_config = mcp_config.to_config() + try: + mcp_server_id = await self.get_mcp_server_id_by_name(mcp_server_name, actor=actor) + mcp_config = await self.get_mcp_server_by_id_async(mcp_server_id, actor=actor) + server_config = mcp_config.to_config() + mcp_client = await self.get_mcp_client(server_config, actor) + await mcp_client.connect_to_server() - mcp_client = await self.get_mcp_client(server_config, actor) - await mcp_client.connect_to_server() - - # list tools - tools = await mcp_client.list_tools() - - # TODO: change to pydantic tools - await mcp_client.cleanup() - - return tools + # list tools + tools = await mcp_client.list_tools() + return tools + except Exception as e: + logger.error(f"Error listing tools for MCP server {mcp_server_name}: {e}") + return [] + finally: + await mcp_client.cleanup() @enforce_types async def execute_mcp_server_tool( From 1a6dfa8668b2dae5480d7a2292d6a28aa452a871 Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Fri, 25 Jul 2025 13:12:59 -0700 Subject: [PATCH 18/24] feat: profiling middleware --- letta/server/rest_api/app.py | 40 +- letta/server/rest_api/middleware/__init__.py | 4 + .../rest_api/middleware/check_password.py | 24 + .../rest_api/middleware/profiler_context.py | 25 + letta/settings.py | 6 + poetry.lock | 723 ++++++++++++++++-- pyproject.toml | 5 +- 7 files changed, 735 insertions(+), 92 deletions(-) create mode 100644 letta/server/rest_api/middleware/__init__.py create mode 100644 letta/server/rest_api/middleware/check_password.py create mode 100644 letta/server/rest_api/middleware/profiler_context.py diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index 208613ec..6cc64dd3 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -12,7 +12,6 @@ from typing import Optional import uvicorn from fastapi import FastAPI, Request from fastapi.responses import JSONResponse -from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.cors import CORSMiddleware from letta.__init__ import __version__ as letta_version @@ -35,6 +34,7 @@ from letta.server.db import db_registry # NOTE(charles): these are extra routes that are not part of v1 but we still need to mount to pass tests from letta.server.rest_api.auth.index import setup_auth_router # TODO: probably remove right? from letta.server.rest_api.interface import StreamingServerInterface +from letta.server.rest_api.middleware import CheckPasswordMiddleware, ProfilerContextMiddleware from letta.server.rest_api.routers.openai.chat_completions.chat_completions import router as openai_chat_completions_router from letta.server.rest_api.routers.v1 import ROUTERS as v1_routes from letta.server.rest_api.routers.v1.organizations import router as organizations_router @@ -42,7 +42,7 @@ from letta.server.rest_api.routers.v1.users import router as users_router # TOD from letta.server.rest_api.static_files import mount_static_files from letta.server.rest_api.utils import SENTRY_ENABLED from letta.server.server import SyncServer -from letta.settings import settings +from letta.settings import settings, telemetry_settings if SENTRY_ENABLED: import sentry_sdk @@ -92,24 +92,6 @@ def generate_password(): random_password = os.getenv("LETTA_SERVER_PASSWORD") or generate_password() -class CheckPasswordMiddleware(BaseHTTPMiddleware): - async def dispatch(self, request, call_next): - # Exclude health check endpoint from password protection - if request.url.path in {"/v1/health", "/v1/health/", "/latest/health/"}: - return await call_next(request) - - if ( - request.headers.get("X-BARE-PASSWORD") == f"password {random_password}" - or request.headers.get("Authorization") == f"Bearer {random_password}" - ): - return await call_next(request) - - return JSONResponse( - content={"detail": "Unauthorized"}, - status_code=401, - ) - - @asynccontextmanager async def lifespan(app_: FastAPI): """ @@ -117,6 +99,19 @@ async def lifespan(app_: FastAPI): """ worker_id = os.getpid() + if telemetry_settings.profiler: + try: + import googlecloudprofiler + + googlecloudprofiler.start( + service="memgpt-server", + service_version=str(letta_version), + verbose=3, + ) + logger.info("Profiler started.") + except (ValueError, NotImplementedError) as exc: + logger.info("Profiler not enabled: %", exc) + logger.info(f"[Worker {worker_id}] Starting lifespan initialization") logger.info(f"[Worker {worker_id}] Initializing database connections") db_registry.initialize_sync() @@ -283,11 +278,14 @@ def create_application() -> "FastAPI": if (os.getenv("LETTA_SERVER_SECURE") == "true") or "--secure" in sys.argv: print(f"▶ Using secure mode with password: {random_password}") - app.add_middleware(CheckPasswordMiddleware) + app.add_middleware(CheckPasswordMiddleware, password=random_password) # Add reverse proxy middleware to handle X-Forwarded-* headers # app.add_middleware(ReverseProxyMiddleware, base_path=settings.server_base_path) + if telemetry_settings.profiler: + app.add_middleware(ProfilerContextMiddleware) + app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, diff --git a/letta/server/rest_api/middleware/__init__.py b/letta/server/rest_api/middleware/__init__.py new file mode 100644 index 00000000..223442c0 --- /dev/null +++ b/letta/server/rest_api/middleware/__init__.py @@ -0,0 +1,4 @@ +from letta.server.rest_api.middleware.check_password import CheckPasswordMiddleware +from letta.server.rest_api.middleware.profiler_context import ProfilerContextMiddleware + +__all__ = ["CheckPasswordMiddleware", "ProfilerContextMiddleware"] diff --git a/letta/server/rest_api/middleware/check_password.py b/letta/server/rest_api/middleware/check_password.py new file mode 100644 index 00000000..f1217e94 --- /dev/null +++ b/letta/server/rest_api/middleware/check_password.py @@ -0,0 +1,24 @@ +from starlette.middleware.base import BaseHTTPMiddleware +from starlette.responses import JSONResponse + + +class CheckPasswordMiddleware(BaseHTTPMiddleware): + def __init__(self, app, password: str): + super().__init__(app) + self.password = password + + async def dispatch(self, request, call_next): + # Exclude health check endpoint from password protection + if request.url.path in {"/v1/health", "/v1/health/", "/latest/health/"}: + return await call_next(request) + + if ( + request.headers.get("X-BARE-PASSWORD") == f"password {self.password}" + or request.headers.get("Authorization") == f"Bearer {self.password}" + ): + return await call_next(request) + + return JSONResponse( + content={"detail": "Unauthorized"}, + status_code=401, + ) diff --git a/letta/server/rest_api/middleware/profiler_context.py b/letta/server/rest_api/middleware/profiler_context.py new file mode 100644 index 00000000..7af66865 --- /dev/null +++ b/letta/server/rest_api/middleware/profiler_context.py @@ -0,0 +1,25 @@ +from starlette.middleware.base import BaseHTTPMiddleware + + +class ProfilerContextMiddleware(BaseHTTPMiddleware): + """Middleware to set context if using profiler. Currently just uses google-cloud-profiler.""" + + async def dispatch(self, request, call_next): + ctx = None + if request.url.path in {"/v1/health", "/v1/health/"}: + return await call_next(request) + try: + labels = { + "method": request.method, + "path": request.url.path, + "endpoint": request.url.path, + } + import googlecloudprofiler + + ctx = googlecloudprofiler.context.set_labels(**labels) + except: + return await call_next(request) + if ctx: + with ctx: + return await call_next(request) + return await call_next(request) diff --git a/letta/settings.py b/letta/settings.py index 3f91b27a..16110d04 100644 --- a/letta/settings.py +++ b/letta/settings.py @@ -331,6 +331,11 @@ class LogSettings(BaseSettings): verbose_telemetry_logging: bool = Field(False) +class TelemetrySettings(BaseSettings): + model_config = SettingsConfigDict(env_prefix="letta_telemetry_", extra="ignore") + profiler: bool | None = Field(False, description="Enable use of the profiler.") + + # singleton settings = Settings(_env_parse_none_str="None") test_settings = TestSettings() @@ -338,3 +343,4 @@ model_settings = ModelSettings() tool_settings = ToolSettings() summarizer_settings = SummarizerSettings() log_settings = LogSettings() +telemetry_settings = TelemetrySettings() diff --git a/poetry.lock b/poetry.lock index b4921d5b..049f04f2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aioboto3" @@ -6,6 +6,8 @@ version = "14.3.0" description = "Async boto3 wrapper" optional = true python-versions = "<4.0,>=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "aioboto3-14.3.0-py3-none-any.whl", hash = "sha256:aec5de94e9edc1ffbdd58eead38a37f00ddac59a519db749a910c20b7b81bca7"}, {file = "aioboto3-14.3.0.tar.gz", hash = "sha256:1d18f88bb56835c607b62bb6cb907754d717bedde3ddfff6935727cb48a80135"}, @@ -25,6 +27,8 @@ version = "2.22.0" description = "Async client for aws services using botocore and aiohttp" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "aiobotocore-2.22.0-py3-none-any.whl", hash = "sha256:b4e6306f79df9d81daff1f9d63189a2dbee4b77ce3ab937304834e35eaaeeccf"}, {file = "aiobotocore-2.22.0.tar.gz", hash = "sha256:11091477266b75c2b5d28421c1f2bc9a87d175d0b8619cb830805e7a113a170b"}, @@ -50,6 +54,8 @@ version = "24.1.0" description = "File support for asyncio." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, @@ -61,6 +67,7 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -72,6 +79,7 @@ version = "3.12.13" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, @@ -172,7 +180,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiohttp-retry" @@ -180,6 +188,8 @@ version = "2.9.1" description = "Simple retry client for aiohttp" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"}, {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"}, @@ -194,6 +204,8 @@ version = "0.12.0" description = "itertools and builtins for AsyncIO and mixed iterables" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, @@ -209,13 +221,14 @@ version = "0.9.1" description = "AsyncIO version of the standard multiprocessing module" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "aiomultiprocess-0.9.1-py3-none-any.whl", hash = "sha256:3a7b3bb3c38dbfb4d9d1194ece5934b6d32cf0280e8edbe64a7d215bba1322c6"}, {file = "aiomultiprocess-0.9.1.tar.gz", hash = "sha256:f0231dbe0291e15325d7896ebeae0002d95a4f2675426ca05eb35f24c60e495b"}, ] [package.extras] -dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0)"] +dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0) ; sys_platform != \"win32\""] docs = ["sphinx (==7.3.7)", "sphinx-mdinclude (==0.6.0)"] [[package]] @@ -224,6 +237,7 @@ version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, @@ -239,6 +253,7 @@ version = "0.21.0" description = "asyncio bridge to the standard sqlite3 module" optional = false python-versions = ">=3.9" +groups = ["main", "sqlite"] files = [ {file = "aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0"}, {file = "aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3"}, @@ -257,6 +272,7 @@ version = "1.16.3" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "alembic-1.16.3-py3-none-any.whl", hash = "sha256:70a7c7829b792de52d08ca0e3aefaf060687cb8ed6bebfa557e597a1a5e5a481"}, {file = "alembic-1.16.3.tar.gz", hash = "sha256:18ad13c1f40a5796deee4b2346d1a9c382f44b8af98053897484fa6cf88025e4"}, @@ -277,6 +293,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -288,6 +305,7 @@ version = "0.49.0" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "anthropic-0.49.0-py3-none-any.whl", hash = "sha256:bbc17ad4e7094988d2fa86b87753ded8dce12498f4b85fe5810f208f454a8375"}, {file = "anthropic-0.49.0.tar.gz", hash = "sha256:c09e885b0f674b9119b4f296d8508907f6cff0009bc20d5cf6b35936c40b4398"}, @@ -312,6 +330,7 @@ version = "4.9.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, @@ -325,7 +344,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] [[package]] @@ -334,6 +353,8 @@ version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_system == \"Darwin\"" files = [ {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, @@ -345,6 +366,7 @@ version = "3.11.0" description = "In-process task scheduler with Cron-like capabilities" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"}, {file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"}, @@ -361,7 +383,7 @@ mongodb = ["pymongo (>=3.0)"] redis = ["redis (>=3.0)"] rethinkdb = ["rethinkdb (>=2.4.0)"] sqlalchemy = ["sqlalchemy (>=1.4)"] -test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6", "anyio (>=4.5.2)", "gevent", "pytest", "pytz", "twisted"] +test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6 ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "anyio (>=4.5.2)", "gevent ; python_version < \"3.14\"", "pytest", "pytz", "twisted ; python_version < \"3.14\""] tornado = ["tornado (>=4.3)"] twisted = ["twisted"] zookeeper = ["kazoo"] @@ -372,6 +394,7 @@ version = "3.6.2" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, @@ -386,6 +409,8 @@ version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" optional = true python-versions = "*" +groups = ["main"] +markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, @@ -397,10 +422,12 @@ version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] astroid = ["astroid (>=2,<4)"] @@ -410,8 +437,10 @@ test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -optional = false +optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "(extra == \"redis\" or extra == \"all\") and python_full_version < \"3.11.3\" or python_version == \"3.10\"" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, @@ -423,6 +452,8 @@ version = "0.30.0" description = "An asyncio PostgreSQL driver" optional = true python-versions = ">=3.8.0" +groups = ["main"] +markers = "extra == \"postgres\"" files = [ {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, @@ -480,8 +511,8 @@ async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.11.0\""} [package.extras] docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] -gssauth = ["gssapi", "sspilib"] -test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] +gssauth = ["gssapi ; platform_system != \"Windows\"", "sspilib ; platform_system == \"Windows\""] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi ; platform_system == \"Linux\"", "k5test ; platform_system == \"Linux\"", "mypy (>=1.8.0,<1.9.0)", "sspilib ; platform_system == \"Windows\"", "uvloop (>=0.15.3) ; platform_system != \"Windows\" and python_version < \"3.14.0\""] [[package]] name = "attrs" @@ -489,18 +520,19 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -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"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "autoflake" @@ -508,6 +540,8 @@ version = "2.3.1" description = "Removes unused imports and unused variables" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840"}, {file = "autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e"}, @@ -523,6 +557,7 @@ version = "2.1.3" description = "A prompt programming language" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "banks-2.1.3-py3-none-any.whl", hash = "sha256:9e1217dc977e6dd1ce42c5ff48e9bcaf238d788c81b42deb6a555615ffcffbab"}, {file = "banks-2.1.3.tar.gz", hash = "sha256:c0dd2cb0c5487274a513a552827e6a8ddbd0ab1a1b967f177e71a6e4748a3ed2"}, @@ -544,6 +579,7 @@ version = "4.3.0" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281"}, {file = "bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb"}, @@ -608,6 +644,7 @@ version = "4.13.4" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" +groups = ["main"] files = [ {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, @@ -630,6 +667,8 @@ version = "0.23.1" description = "The bidirectional mapping library for Python." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"}, {file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"}, @@ -641,6 +680,7 @@ version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, @@ -689,6 +729,8 @@ version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, @@ -700,6 +742,8 @@ version = "1.37.3" description = "The AWS SDK for Python" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "boto3-1.37.3-py3-none-any.whl", hash = "sha256:2063b40af99fd02f6228ff52397b552ff3353831edaf8d25cc04801827ab9794"}, {file = "boto3-1.37.3.tar.gz", hash = "sha256:21f3ce0ef111297e63a6eb998a25197b8c10982970c320d4c6e8db08be2157be"}, @@ -719,6 +763,8 @@ version = "1.37.3" description = "Low-level, data-driven core of boto 3." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "botocore-1.37.3-py3-none-any.whl", hash = "sha256:d01bd3bf4c80e61fa88d636ad9f5c9f60a551d71549b481386c6b4efe0bb2b2e"}, {file = "botocore-1.37.3.tar.gz", hash = "sha256:fe8403eb55a88faf9b0f9da6615e5bee7be056d75e17af66c3c8f0a3b0648da4"}, @@ -738,6 +784,7 @@ version = "1.1.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, @@ -749,6 +796,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -761,8 +812,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -773,8 +830,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -784,6 +857,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -795,6 +872,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -807,6 +888,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -819,6 +904,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -830,6 +919,8 @@ version = "5.5.2" description = "Extensible memoizing collections and decorators" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"google\" or extra == \"experimental\" or extra == \"all\"" files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -841,6 +932,7 @@ version = "2025.7.9" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39"}, {file = "certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079"}, @@ -852,6 +944,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -921,6 +1014,7 @@ files = [ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] +markers = {dev = "implementation_name == \"pypy\""} [package.dependencies] pycparser = "*" @@ -931,6 +1025,8 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -942,6 +1038,7 @@ version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -1043,6 +1140,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -1057,6 +1155,7 @@ version = "0.1.4" description = "Create data objects" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "cobble-0.1.4-py3-none-any.whl", hash = "sha256:36c91b1655e599fd428e2b95fdd5f0da1ca2e9f1abb0bc871dec21a0e78a2b44"}, {file = "cobble-0.1.4.tar.gz", hash = "sha256:de38be1539992c8a06e569630717c485a5f91be2192c461ea2b220607dfa78aa"}, @@ -1068,6 +1167,7 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev", "dev,tests"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -1079,6 +1179,7 @@ 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.*" +groups = ["main"] files = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, @@ -1096,6 +1197,7 @@ version = "0.2.2" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, @@ -1113,6 +1215,7 @@ version = "0.7.20" description = "Core package to act as a bridge between composio platform and other services." optional = false python-versions = "<4,>=3.9" +groups = ["main"] files = [ {file = "composio_core-0.7.20-py3-none-any.whl", hash = "sha256:e1cfb9cfc68a4622bc15827143ddf726f429d281e8f9de5d4c0965e75d039f14"}, {file = "composio_core-0.7.20.tar.gz", hash = "sha256:1dc29dbf73eb72d2df1c5b0d4d2f21459d15029322cf74df8fdecc44dcaeb1f4"}, @@ -1151,6 +1254,8 @@ version = "1.7.1" description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." optional = true python-versions = ">=3.6" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6"}, {file = "configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9"}, @@ -1166,6 +1271,7 @@ version = "1.3.2" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934"}, {file = "contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989"}, @@ -1242,6 +1348,7 @@ version = "45.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" +groups = ["main"] files = [ {file = "cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8"}, {file = "cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d"}, @@ -1286,10 +1393,10 @@ files = [ cffi = {version = ">=1.14", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs ; python_full_version >= \"3.8.0\"", "sphinx-rtd-theme (>=3.0.0) ; python_full_version >= \"3.8.0\""] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_full_version >= \"3.8.0\""] +pep8test = ["check-sdist ; python_full_version >= \"3.8.0\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==45.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -1301,6 +1408,7 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -1316,6 +1424,7 @@ version = "0.6.7" description = "Easily serialize dataclasses to and from JSON." optional = false python-versions = "<4.0,>=3.7" +groups = ["main"] files = [ {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, @@ -1331,6 +1440,7 @@ version = "0.25.9" description = "Datamodel Code Generator" optional = false python-versions = "<4.0,>=3.7" +groups = ["main"] files = [ {file = "datamodel_code_generator-0.25.9-py3-none-any.whl", hash = "sha256:9e0324233123d6e39a35bc0004771956935889a974aacfd7a0651de11d2219a9"}, {file = "datamodel_code_generator-0.25.9.tar.gz", hash = "sha256:65ca9807d8edbd88a7f7931c10f4bc1c08bd9bbc5bb0508418a2b6a16590eb65"}, @@ -1346,9 +1456,9 @@ isort = ">=4.3.21,<6.0" jinja2 = ">=2.10.1,<4.0" packaging = "*" pydantic = [ - {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, - {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.10.0,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, + {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"4.0\""}, + {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version == \"3.10\""}, ] pyyaml = ">=6.0.1" toml = {version = ">=0.10.0,<1.0.0", markers = "python_version < \"3.11\""} @@ -1365,6 +1475,7 @@ version = "1.8.14" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "debugpy-1.8.14-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:93fee753097e85623cab1c0e6a68c76308cd9f13ffdf44127e6fab4fbf024339"}, {file = "debugpy-1.8.14-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d937d93ae4fa51cdc94d3e865f535f185d5f9748efb41d0d49e33bf3365bd79"}, @@ -1400,10 +1511,12 @@ version = "5.2.1" description = "Decorators for Humans" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [[package]] name = "defusedxml" @@ -1411,6 +1524,7 @@ version = "0.7.1" description = "XML bomb protection for Python stdlib modules" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, @@ -1422,6 +1536,7 @@ version = "3.0.6" description = "encoder, decoder, and lint/validator for JSON (JavaScript Object Notation) compliant with RFC 7159" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "demjson3-3.0.6.tar.gz", hash = "sha256:37c83b0c6eb08d25defc88df0a2a4875d58a7809a9650bd6eee7afd8053cdbac"}, ] @@ -1432,6 +1547,7 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["main"] files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -1441,7 +1557,7 @@ files = [ wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] [[package]] name = "dirtyjson" @@ -1449,6 +1565,7 @@ version = "1.0.8" description = "JSON decoder for Python that can extract data from the muck" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53"}, {file = "dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd"}, @@ -1460,6 +1577,8 @@ version = "0.3.9" description = "Distribution utilities" optional = true python-versions = "*" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -1471,6 +1590,7 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -1482,6 +1602,7 @@ version = "2.7.0" description = "DNS toolkit" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -1502,6 +1623,8 @@ version = "7.1.0" description = "A Python library for the Docker Engine API." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, @@ -1524,6 +1647,7 @@ version = "0.16" description = "Parse Python docstrings in reST, Google and Numpydoc format" optional = false python-versions = ">=3.6,<4.0" +groups = ["main"] files = [ {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, @@ -1535,6 +1659,8 @@ version = "1.5.5" description = "E2B SDK that give agents cloud environments" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "extra == \"cloud-tool-sandbox\"" files = [ {file = "e2b-1.5.5-py3-none-any.whl", hash = "sha256:cd6343193f5b941af33504d422cb39c02515a23a5c94c9ef9fdebeb140fb382e"}, {file = "e2b-1.5.5.tar.gz", hash = "sha256:541dd0bd8b3ff8aa1f56a2719333d5b8e0465c30df7ebd93e2776cc15672503a"}, @@ -1555,6 +1681,8 @@ version = "1.5.2" description = "E2B Code Interpreter - Stateful code execution" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "extra == \"cloud-tool-sandbox\"" files = [ {file = "e2b_code_interpreter-1.5.2-py3-none-any.whl", hash = "sha256:5c3188d8f25226b28fef4b255447cc6a4c36afb748bdd5180b45be486d5169f3"}, {file = "e2b_code_interpreter-1.5.2.tar.gz", hash = "sha256:3bd6ea70596290e85aaf0a2f19f28bf37a5e73d13086f5e6a0080bb591c5a547"}, @@ -1571,6 +1699,7 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -1586,6 +1715,7 @@ version = "0.2.2" description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, @@ -1600,6 +1730,8 @@ version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["main", "dev", "dev,tests"] +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -1617,13 +1749,15 @@ version = "2.2.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"}, {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "faker" @@ -1631,6 +1765,7 @@ version = "36.2.3" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "Faker-36.2.3-py3-none-any.whl", hash = "sha256:7ca995d65ec08d013f3c1230da7f628cb2c169a77e89cd265d7a59f443f0febd"}, {file = "faker-36.2.3.tar.gz", hash = "sha256:1ed2d7a9c3a5657fc11a4298e8cf19f71d83740560d4ed0895b30399d482d538"}, @@ -1645,6 +1780,7 @@ version = "0.115.14" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, @@ -1665,6 +1801,8 @@ version = "3.18.0" description = "A platform independent file lock." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, @@ -1673,7 +1811,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "filetype" @@ -1681,6 +1819,7 @@ version = "1.2.0" description = "Infer file type and MIME type of any file/buffer. No external dependencies." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, @@ -1692,6 +1831,8 @@ version = "2.15.0" description = "Python SDK for Firecrawl API" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"external-tools\"" files = [ {file = "firecrawl_py-2.15.0-py3-none-any.whl", hash = "sha256:6e4c53f029fe4784854549cf2760a7ea6a7faa2a098ce9fd49dd85e2f7004186"}, {file = "firecrawl_py-2.15.0.tar.gz", hash = "sha256:8bc8eea586f1fc81bac8692faa34f362050ff8045298b71d04140239c843349b"}, @@ -1711,6 +1852,8 @@ version = "3.1.1" description = "A simple framework for building complex web applications." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, @@ -1734,6 +1877,8 @@ version = "6.0.1" description = "A Flask extension simplifying CORS support" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "flask_cors-6.0.1-py3-none-any.whl", hash = "sha256:c7b2cbfb1a31aa0d2e5341eea03a6805349f7a61647daee1a15c46bbe981494c"}, {file = "flask_cors-6.0.1.tar.gz", hash = "sha256:d81bcb31f07b0985be7f48406247e9243aced229b7747219160a0559edd678db"}, @@ -1749,6 +1894,8 @@ version = "0.6.3" description = "User authentication and session management for Flask." optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333"}, {file = "Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d"}, @@ -1764,6 +1911,7 @@ version = "25.2.10" description = "The FlatBuffers serialization format for Python" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051"}, {file = "flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e"}, @@ -1775,6 +1923,7 @@ version = "4.58.5" description = "Tools to manipulate font files" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d500d399aa4e92d969a0d21052696fa762385bb23c3e733703af4a195ad9f34c"}, {file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b00530b84f87792891874938bd42f47af2f7f4c2a1d70466e6eb7166577853ab"}, @@ -1821,18 +1970,18 @@ files = [ ] [package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "pycairo", "scipy"] +interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""] lxml = ["lxml (>=4.0)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] repacker = ["uharfbuzz (>=0.23.0)"] symfont = ["sympy"] -type1 = ["xattr"] +type1 = ["xattr ; sys_platform == \"darwin\""] ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0)"] -woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] +woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] [[package]] name = "frozenlist" @@ -1840,6 +1989,7 @@ version = "1.7.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, @@ -1953,6 +2103,7 @@ version = "2025.5.1" description = "File-system specification" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462"}, {file = "fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475"}, @@ -1992,6 +2143,7 @@ version = "1.3.0" description = "GenSON is a powerful, user-friendly JSON Schema generator." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7"}, {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, @@ -2003,6 +2155,8 @@ version = "25.5.1" description = "Coroutine-based network library" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "gevent-25.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8e5a0fab5e245b15ec1005b3666b0a2e867c26f411c8fe66ae1afe07174a30e9"}, {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7b80a37f2fb45ee4a8f7e64b77dd8a842d364384046e394227b974a4e9c9a52"}, @@ -2052,11 +2206,11 @@ greenlet = {version = ">=3.2.2", markers = "platform_python_implementation == \" "zope.interface" = "*" [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] +dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] +monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] +recommended = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] +test = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"] [[package]] name = "geventhttpclient" @@ -2064,6 +2218,8 @@ version = "2.3.4" description = "HTTP client library for gevent" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "geventhttpclient-2.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:182f5158504ac426d591cfb1234de5180813292b49049e761f00bf70691aace5"}, {file = "geventhttpclient-2.3.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59a2e7c136a3e6b60b87bf8b87e5f1fb25705d76ab7471018e25f8394c640dda"}, @@ -2162,12 +2318,63 @@ benchmarks = ["httplib2", "httpx", "requests", "urllib3"] dev = ["dpkt", "pytest", "requests"] examples = ["oauth2"] +[[package]] +name = "google-api-core" +version = "2.25.1" +description = "Google API client core library" +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "google_api_core-2.25.1-py3-none-any.whl", hash = "sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7"}, + {file = "google_api_core-2.25.1.tar.gz", hash = "sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.0" +googleapis-common-protos = ">=1.56.2,<2.0.0" +proto-plus = [ + {version = ">=1.22.3,<2.0.0"}, + {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, +] +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,<7.0.0" +requests = ">=2.18.0,<3.0.0" + +[package.extras] +async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.0)"] +grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0) ; python_version >= \"3.11\""] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] + +[[package]] +name = "google-api-python-client" +version = "2.177.0" +description = "Google API Client Library for Python" +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "google_api_python_client-2.177.0-py3-none-any.whl", hash = "sha256:f2f50f11105ab883eb9b6cf38ec54ea5fd4b429249f76444bec90deba5be79b3"}, + {file = "google_api_python_client-2.177.0.tar.gz", hash = "sha256:9ffd2b57d68f5afa7e6ac64e2c440534eaa056cbb394812a62ff94723c31b50e"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0" +google-auth = ">=1.32.0,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" +google-auth-httplib2 = ">=0.2.0,<1.0.0" +httplib2 = ">=0.19.0,<1.0.0" +uritemplate = ">=3.0.1,<5" + [[package]] name = "google-auth" version = "2.40.3" description = "Google Authentication Library" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"google\" or extra == \"experimental\" or extra == \"all\"" files = [ {file = "google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca"}, {file = "google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77"}, @@ -2181,19 +2388,57 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"] enterprise-cert = ["cryptography", "pyopenssl"] -pyjwt = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"] -pyopenssl = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +pyjwt = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"] +pyopenssl = ["cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0)"] -testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0)", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"] +testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0) ; python_version < \"3.8\"", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"] urllib3 = ["packaging", "urllib3"] +[[package]] +name = "google-auth-httplib2" +version = "0.2.0" +description = "Google Authentication Library: httplib2 transport" +optional = true +python-versions = "*" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"}, + {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.19.0" + +[[package]] +name = "google-cloud-profiler" +version = "4.1.0" +description = "Google Cloud Profiler Python Agent" +optional = true +python-versions = "*" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "google-cloud-profiler-4.1.0.tar.gz", hash = "sha256:2d90f9c6d4c075ad6d43752ae39424c3d0bad63e6549a2c761881f9a235067ae"}, +] + +[package.dependencies] +google-api-python-client = "<1.12.0 || >1.12.0,<2.0.2 || >2.0.2" +google-auth = ">=1.0.0" +google-auth-httplib2 = "*" +protobuf = ">=3.20" +requests = "*" + [[package]] name = "google-genai" version = "1.25.0" description = "GenAI Python SDK" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"google\"" files = [ {file = "google_genai-1.25.0-py3-none-any.whl", hash = "sha256:fb5cee79b9a0a1b2afd5cfdf279099ecebd186551eefcaa6ec0c6016244e6138"}, {file = "google_genai-1.25.0.tar.gz", hash = "sha256:a08a79c819a5d949d9948cd372e36e512bf85cd28158994daaa36d0ec4cb2b02"}, @@ -2218,6 +2463,7 @@ version = "1.70.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, @@ -2235,6 +2481,8 @@ version = "2.4.1" description = "A Rust HTTP server for Python applications" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "granian-2.4.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7a5279a4d6664f1aa60826af6e3588d890732067c8f6266946d9810452e616ea"}, {file = "granian-2.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:42c93f33914d9de8f79ce4bfe50f8b640733865831c4ec020199c9c57bf52cfd"}, @@ -2349,8 +2597,8 @@ all = ["granian[dotenv,pname,reload]"] dotenv = ["python-dotenv (>=1.1,<2.0)"] pname = ["setproctitle (>=1.3.3,<1.4.0)"] reload = ["watchfiles (>=1.0,<2.0)"] -rloop = ["rloop (>=0.1,<1.0)"] -uvloop = ["uvloop (>=0.18.0)"] +rloop = ["rloop (>=0.1,<1.0) ; sys_platform != \"win32\""] +uvloop = ["uvloop (>=0.18.0) ; platform_python_implementation == \"CPython\" and sys_platform != \"win32\""] [[package]] name = "greenlet" @@ -2358,6 +2606,8 @@ version = "3.2.3" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or (extra == \"dev\" or extra == \"desktop\" or extra == \"all\") and platform_python_implementation == \"CPython\"" files = [ {file = "greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be"}, {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac"}, @@ -2425,6 +2675,7 @@ version = "1.7.3" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75"}, {file = "griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b"}, @@ -2439,6 +2690,7 @@ version = "1.73.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "grpcio-1.73.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:2d70f4ddd0a823436c2624640570ed6097e40935c9194482475fe8e3d9754d55"}, {file = "grpcio-1.73.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:3841a8a5a66830261ab6a3c2a3dc539ed84e4ab019165f77b3eeb9f0ba621f26"}, @@ -2502,6 +2754,7 @@ version = "1.71.2" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "grpcio_tools-1.71.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:ab8a28c2e795520d6dc6ffd7efaef4565026dbf9b4f5270de2f3dd1ce61d2318"}, {file = "grpcio_tools-1.71.2-cp310-cp310-macosx_10_14_universal2.whl", hash = "sha256:654ecb284a592d39a85556098b8c5125163435472a20ead79b805cf91814b99e"}, @@ -2567,6 +2820,7 @@ version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, @@ -2578,6 +2832,7 @@ version = "2020.1.16" description = "Turn HTML into equivalent Markdown-structured text." optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, @@ -2589,6 +2844,7 @@ version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, @@ -2604,12 +2860,29 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<1.0)"] +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + [[package]] name = "httpx" version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -2622,7 +2895,7 @@ httpcore = "==1.*" idna = "*" [package.extras] -brotli = ["brotli", "brotlicffi"] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] @@ -2634,6 +2907,7 @@ version = "0.4.0" description = "Consume Server-Sent Event (SSE) messages with HTTPX." optional = false python-versions = ">=3.8" +groups = ["main"] 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"}, @@ -2645,6 +2919,7 @@ 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.*" +groups = ["main"] files = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, @@ -2659,6 +2934,8 @@ version = "2.6.12" description = "File identification library for Python" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"}, {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"}, @@ -2673,6 +2950,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -2687,6 +2965,7 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -2696,12 +2975,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -2710,6 +2989,7 @@ version = "5.6.2" description = "Correctly generate plurals, singular nouns, ordinals, indefinite articles; convert numbers to words" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238"}, {file = "inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9"}, @@ -2717,7 +2997,7 @@ files = [ [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] [[package]] name = "inflection" @@ -2725,6 +3005,7 @@ version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, @@ -2736,10 +3017,12 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "dev,tests"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [[package]] name = "ipdb" @@ -2747,14 +3030,15 @@ version = "0.13.13" description = "IPython-enabled pdb" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] files = [ {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, ] [package.dependencies] -decorator = {version = "*", markers = "python_version > \"3.6\""} -ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""} +decorator = {version = "*", markers = "python_version >= \"3.11\""} +ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} [[package]] @@ -2763,6 +3047,7 @@ version = "6.29.5" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, @@ -2796,10 +3081,12 @@ version = "8.37.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" +groups = ["main", "dev"] files = [ {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"}, {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} @@ -2817,7 +3104,7 @@ typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -2834,6 +3121,7 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -2848,6 +3136,8 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -2859,10 +3149,12 @@ version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] parso = ">=0.8.4,<0.9.0" @@ -2878,6 +3170,7 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -2895,6 +3188,7 @@ version = "0.10.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303"}, {file = "jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e"}, @@ -2981,6 +3275,8 @@ version = "1.0.1" description = "JSON Matching Expressions" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, @@ -2992,6 +3288,7 @@ version = "1.5.1" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a"}, {file = "joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444"}, @@ -3003,6 +3300,8 @@ version = "1.33" description = "Apply JSON-Patches (RFC 6902)" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, @@ -3017,6 +3316,8 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -3028,6 +3329,7 @@ version = "1.1.0" description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, @@ -3039,6 +3341,7 @@ version = "4.24.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d"}, {file = "jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196"}, @@ -3060,6 +3363,7 @@ version = "2025.4.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, @@ -3074,6 +3378,7 @@ version = "8.6.3" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, @@ -3088,7 +3393,7 @@ traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-core" @@ -3096,6 +3401,7 @@ version = "5.8.1" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0"}, {file = "jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941"}, @@ -3116,6 +3422,7 @@ version = "1.4.8" description = "A fast implementation of the Cassowary constraint solver" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, @@ -3205,6 +3512,8 @@ version = "0.3.26" description = "Building applications with LLMs through composability" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain-0.3.26-py3-none-any.whl", hash = "sha256:361bb2e61371024a8c473da9f9c55f4ee50f269c5ab43afdb2b1309cb7ac36cf"}, {file = "langchain-0.3.26.tar.gz", hash = "sha256:8ff034ee0556d3e45eff1f1e96d0d745ced57858414dba7171c8ebdbeb5580c9"}, @@ -3245,6 +3554,8 @@ version = "0.3.27" description = "Community contributed LangChain integrations." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_community-0.3.27-py3-none-any.whl", hash = "sha256:581f97b795f9633da738ea95da9cb78f8879b538090c9b7a68c0aed49c828f0d"}, {file = "langchain_community-0.3.27.tar.gz", hash = "sha256:e1037c3b9da0c6d10bf06e838b034eb741e016515c79ef8f3f16e53ead33d882"}, @@ -3273,6 +3584,8 @@ version = "0.3.68" description = "Building applications with LLMs through composability" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_core-0.3.68-py3-none-any.whl", hash = "sha256:5e5c1fbef419590537c91b8c2d86af896fbcbaf0d5ed7fdcdd77f7d8f3467ba0"}, {file = "langchain_core-0.3.68.tar.gz", hash = "sha256:312e1932ac9aa2eaf111b70fdc171776fa571d1a86c1f873dcac88a094b19c6f"}, @@ -3293,6 +3606,8 @@ version = "0.3.8" description = "LangChain text splitting utilities" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02"}, {file = "langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e"}, @@ -3307,6 +3622,8 @@ version = "0.4.4" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "langsmith-0.4.4-py3-none-any.whl", hash = "sha256:014c68329bd085bd6c770a6405c61bb6881f82eb554ce8c4d1984b0035fd1716"}, {file = "langsmith-0.4.4.tar.gz", hash = "sha256:70c53bbff24a7872e88e6fa0af98270f4986a6e364f9e85db1cc5636defa4d66"}, @@ -3333,6 +3650,7 @@ version = "0.1.220" description = "" optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "letta_client-0.1.220-py3-none-any.whl", hash = "sha256:ba5faaadc2f8819d846ca2381dc3d757525f6af97822fba31f0643edd521eeef"}, {file = "letta_client-0.1.220.tar.gz", hash = "sha256:cebc7e31cae7887d7aa684803a6c4679fa9e1ecdc1e1572f1b23934969f69523"}, @@ -3351,6 +3669,7 @@ version = "0.1.32" description = "" optional = false python-versions = "<4,>=3.8" +groups = ["main"] files = [ {file = "llama_cloud-0.1.32-py3-none-any.whl", hash = "sha256:c42b2d5fb24acc8595bcc3626fb84c872909a16ab6d6879a1cb1101b21c238bd"}, {file = "llama_cloud-0.1.32.tar.gz", hash = "sha256:cea98241127311ea91f191c3c006aa6558f01d16f9539ed93b24d716b888f10e"}, @@ -3367,6 +3686,7 @@ version = "0.6.43" description = "Tailored SDK clients for LlamaCloud services." optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_cloud_services-0.6.43-py3-none-any.whl", hash = "sha256:2349195f501ba9151ea3ab384d20cae8b4dc4f335f60bd17607332626bdfa2e4"}, {file = "llama_cloud_services-0.6.43.tar.gz", hash = "sha256:fa6be33bf54d467cace809efee8c2aeeb9de74ce66708513d37b40d738d3350f"}, @@ -3387,6 +3707,7 @@ version = "0.12.42" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index-0.12.42-py3-none-any.whl", hash = "sha256:9a304b2bd71d6772fc6c2e9995e119815c00716fc98813b15ea79caee09f1fe2"}, {file = "llama_index-0.12.42.tar.gz", hash = "sha256:d2bd2b9ea06bc42ebe74697aaa0b66c79bd3dee8ff5d06c53294d49ca44512e7"}, @@ -3412,6 +3733,7 @@ version = "0.4.12" description = "llama-index agent openai integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_agent_openai-0.4.12-py3-none-any.whl", hash = "sha256:6dbb6276b2e5330032a726b28d5eef5140825f36d72d472b231f08ad3af99665"}, {file = "llama_index_agent_openai-0.4.12.tar.gz", hash = "sha256:d2fe53feb69cfe45752edb7328bf0d25f6a9071b3c056787e661b93e5b748a28"}, @@ -3428,6 +3750,7 @@ version = "0.4.4" description = "llama-index cli" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_cli-0.4.4-py3-none-any.whl", hash = "sha256:1070593cf79407054735ab7a23c5a65a26fc18d264661e42ef38fc549b4b7658"}, {file = "llama_index_cli-0.4.4.tar.gz", hash = "sha256:c3af0cf1e2a7e5ef44d0bae5aa8e8872b54c5dd6b731afbae9f13ffeb4997be0"}, @@ -3444,6 +3767,7 @@ version = "0.12.42" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_core-0.12.42-py3-none-any.whl", hash = "sha256:0534cd9a4f6113175aa406a47ae9a683b5a43fd55532e9dbbffa96838ff18e07"}, {file = "llama_index_core-0.12.42.tar.gz", hash = "sha256:cff21fe15610826997c876a6b1d28d52727932c5f9c2af04b23e041a10f40a24"}, @@ -3481,6 +3805,7 @@ version = "0.3.1" description = "llama-index embeddings openai integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_embeddings_openai-0.3.1-py3-none-any.whl", hash = "sha256:f15a3d13da9b6b21b8bd51d337197879a453d1605e625a1c6d45e741756c0290"}, {file = "llama_index_embeddings_openai-0.3.1.tar.gz", hash = "sha256:1368aad3ce24cbaed23d5ad251343cef1eb7b4a06d6563d6606d59cb347fef20"}, @@ -3496,6 +3821,7 @@ version = "0.7.10" description = "llama-index indices llama-cloud integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_indices_managed_llama_cloud-0.7.10-py3-none-any.whl", hash = "sha256:f7edcfb8f694cab547cd9324be7835dc97470ce05150d0b8888fa3bf9d2f84a8"}, {file = "llama_index_indices_managed_llama_cloud-0.7.10.tar.gz", hash = "sha256:53267907e23d8fbcbb97c7a96177a41446de18550ca6030276092e73b45ca880"}, @@ -3511,6 +3837,7 @@ version = "0.4.7" description = "llama-index llms openai integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_llms_openai-0.4.7-py3-none-any.whl", hash = "sha256:3b8d9d3c1bcadc2cff09724de70f074f43eafd5b7048a91247c9a41b7cd6216d"}, {file = "llama_index_llms_openai-0.4.7.tar.gz", hash = "sha256:564af8ab39fb3f3adfeae73a59c0dca46c099ab844a28e725eee0c551d4869f8"}, @@ -3526,6 +3853,7 @@ version = "0.5.1" description = "llama-index multi-modal-llms openai integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_multi_modal_llms_openai-0.5.1-py3-none-any.whl", hash = "sha256:69bb9c310c323ce51038f0d40719f546d0d4e0853835a4f5cfa21f07dbb0b91e"}, {file = "llama_index_multi_modal_llms_openai-0.5.1.tar.gz", hash = "sha256:df3aff00c36023c5f8c49f972a325f71823ed0f4dd9cd479955d76afc146575f"}, @@ -3541,6 +3869,7 @@ version = "0.3.2" description = "llama-index program openai integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_program_openai-0.3.2-py3-none-any.whl", hash = "sha256:451829ae53e074e7b47dcc60a9dd155fcf9d1dcbc1754074bdadd6aab4ceb9aa"}, {file = "llama_index_program_openai-0.3.2.tar.gz", hash = "sha256:04c959a2e616489894bd2eeebb99500d6f1c17d588c3da0ddc75ebd3eb7451ee"}, @@ -3557,6 +3886,7 @@ version = "0.3.1" description = "llama-index question_gen openai integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_question_gen_openai-0.3.1-py3-none-any.whl", hash = "sha256:1ce266f6c8373fc8d884ff83a44dfbacecde2301785db7144872db51b8b99429"}, {file = "llama_index_question_gen_openai-0.3.1.tar.gz", hash = "sha256:5e9311b433cc2581ff8a531fa19fb3aa21815baff75aaacdef11760ac9522aa9"}, @@ -3573,6 +3903,7 @@ version = "0.4.11" description = "llama-index readers file integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_readers_file-0.4.11-py3-none-any.whl", hash = "sha256:e71192d8d6d0bf95131762da15fa205cf6e0cc248c90c76ee04d0fbfe160d464"}, {file = "llama_index_readers_file-0.4.11.tar.gz", hash = "sha256:1b21cb66d78dd5f60e8716607d9a47ccd81bb39106d459665be1ca7799e9597b"}, @@ -3595,6 +3926,7 @@ version = "0.4.0" description = "llama-index readers llama-parse integration" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_index_readers_llama_parse-0.4.0-py3-none-any.whl", hash = "sha256:574e48386f28d2c86c3f961ca4a4906910312f3400dd0c53014465bfbc6b32bf"}, {file = "llama_index_readers_llama_parse-0.4.0.tar.gz", hash = "sha256:e99ec56f4f8546d7fda1a7c1ae26162fb9acb7ebcac343b5abdb4234b4644e0f"}, @@ -3610,6 +3942,7 @@ version = "0.6.43" description = "Parse files into RAG-Optimized formats." optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "llama_parse-0.6.43-py3-none-any.whl", hash = "sha256:fe435309638c4fdec4fec31f97c5031b743c92268962d03b99bd76704f566c32"}, {file = "llama_parse-0.6.43.tar.gz", hash = "sha256:d88e91c97e37f77b2619111ef43c02b7da61125f821cf77f918996eb48200d78"}, @@ -3624,6 +3957,8 @@ version = "2.37.12" description = "Developer-friendly load testing framework" optional = true python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "locust-2.37.12-py3-none-any.whl", hash = "sha256:7866567a3e38103fb5db096864701f99e9136617269c799158518ccbf1c98982"}, {file = "locust-2.37.12.tar.gz", hash = "sha256:e62fb5230cc279c87114e180facf429f0717074b68e08e5a46226419e6674ee6"}, @@ -3642,8 +3977,8 @@ psutil = ">=5.9.1" pywin32 = {version = "*", markers = "sys_platform == \"win32\""} pyzmq = ">=25.0.0" requests = [ - {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, {version = ">=2.32.2", markers = "python_version > \"3.11\""}, + {version = ">=2.26.0", markers = "python_version <= \"3.11\""}, ] setuptools = ">=70.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -3656,6 +3991,8 @@ version = "1.25.1" description = "Locust Cloud" optional = true python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "locust_cloud-1.25.1-py3-none-any.whl", hash = "sha256:0b9e562d2521a6efa9e2f97c2d6ef6d9807ca56868ed9c8d73827fe94f2b94f3"}, {file = "locust_cloud-1.25.1.tar.gz", hash = "sha256:f248e3929a2021a1ea178253b77c56e32ef882899f90bd7c6f762522abdbd769"}, @@ -3675,6 +4012,7 @@ version = "6.0.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:35bc626eec405f745199200ccb5c6b36f202675d204aa29bb52e27ba2b71dea8"}, {file = "lxml-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:246b40f8a4aec341cbbf52617cad8ab7c888d944bfe12a6abd2b1f6cfb6f6082"}, @@ -3784,6 +4122,7 @@ version = "0.6.2" description = "A tool to determine the content type of a file with deep learning" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "magika-0.6.2-py3-none-any.whl", hash = "sha256:5ef72fbc07723029b3684ef81454bc224ac5f60986aa0fc5a28f4456eebcb5b2"}, {file = "magika-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9109309328a1553886c8ff36c2ee9a5e9cfd36893ad81b65bf61a57debdd9d0e"}, @@ -3795,9 +4134,9 @@ files = [ [package.dependencies] click = ">=8.1.7" numpy = [ - {version = ">=1.24", markers = "python_version < \"3.12\""}, - {version = ">=1.26", markers = "python_version >= \"3.12\" and python_version < \"3.13\""}, + {version = ">=1.26", markers = "python_version == \"3.12\""}, {version = ">=2.1.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.24", markers = "python_version < \"3.12\""}, ] onnxruntime = {version = ">=1.17.0", markers = "python_version > \"3.9\""} python-dotenv = ">=1.0.1" @@ -3808,6 +4147,7 @@ version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, @@ -3827,6 +4167,7 @@ version = "1.9.1" description = "Convert Word documents from docx to simple and clean HTML and Markdown" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mammoth-1.9.1-py2.py3-none-any.whl", hash = "sha256:f0569bd640cee6c77a07e7c75c5dc10d745dc4dc95d530cfcbb0a5d9536d636c"}, {file = "mammoth-1.9.1.tar.gz", hash = "sha256:7924254ab8f03efe55fadc0fd5f7828db831190eb2679d63cb4372873e71c572"}, @@ -3841,6 +4182,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -3865,6 +4207,7 @@ version = "1.1.0" description = "Convert HTML to markdown." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "markdownify-1.1.0-py3-none-any.whl", hash = "sha256:32a5a08e9af02c8a6528942224c91b933b4bd2c7d078f9012943776fc313eeef"}, {file = "markdownify-1.1.0.tar.gz", hash = "sha256:449c0bbbf1401c5112379619524f33b63490a8fa479456d41de9dc9e37560ebd"}, @@ -3880,6 +4223,7 @@ version = "0.1.2" description = "Utility tool for converting various files to Markdown" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "markitdown-0.1.2-py3-none-any.whl", hash = "sha256:4881f0768794ffccb52d09dd86498813a6896ba9639b4fc15512817f56ed9d74"}, {file = "markitdown-0.1.2.tar.gz", hash = "sha256:85fe108a92bd18f317e75a36cf567a6fa812072612a898abf8c156d5d74c13c4"}, @@ -3915,6 +4259,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -3985,6 +4330,7 @@ version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, @@ -4004,6 +4350,7 @@ version = "1.4.2" description = "SQLAlchemy integration with the marshmallow (de)serialization library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "marshmallow_sqlalchemy-1.4.2-py3-none-any.whl", hash = "sha256:65aee301c4601e76a2fdb02764a65c18913afba2a3506a326c625d13ab405b40"}, {file = "marshmallow_sqlalchemy-1.4.2.tar.gz", hash = "sha256:6410304bf98ec26ea35f3f9d3cee82e51fd093c434612add32a0bdcdb5668f7c"}, @@ -4015,7 +4362,7 @@ SQLAlchemy = ">=1.4.40,<3.0" [package.extras] dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<5.0)", "tox"] -docs = ["furo (==2024.8.6)", "sphinx (==8.2.3)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.10.0)"] +docs = ["furo (==2024.8.6)", "sphinx (==8.2.3) ; python_version >= \"3.11\"", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.10.0)"] tests = ["pytest (<9)", "pytest-lazy-fixtures"] [[package]] @@ -4024,6 +4371,7 @@ version = "3.10.3" description = "Python plotting package" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7"}, {file = "matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb"}, @@ -4081,10 +4429,12 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] traitlets = "*" @@ -4095,6 +4445,7 @@ version = "1.10.1" description = "Model Context Protocol SDK" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "mcp-1.10.1-py3-none-any.whl", hash = "sha256:4d08301aefe906dce0fa482289db55ce1db831e3e67212e65b5e23ad8454b3c5"}, {file = "mcp-1.10.1.tar.gz", hash = "sha256:aaa0957d8307feeff180da2d9d359f2b801f35c0c67f1882136239055ef034c2"}, @@ -4125,6 +4476,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -4136,6 +4488,7 @@ version = "1.9.1" description = "Python Client SDK for the Mistral AI API." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "mistralai-1.9.1-py3-none-any.whl", hash = "sha256:250ec26534db6f4a4d5e6292b0801a64da2ab1f0d4c63a20d8ce27e3a427e402"}, {file = "mistralai-1.9.1.tar.gz", hash = "sha256:89eb1d48e9555c8289c02ddea966115eba0516355731726ea0a24eabb42f8419"}, @@ -4149,7 +4502,7 @@ python-dateutil = ">=2.8.2" typing-inspection = ">=0.4.0" [package.extras] -agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0)"] +agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0) ; python_version >= \"3.10\""] gcp = ["google-auth (>=2.27.0)", "requests (>=2.32.3)"] [[package]] @@ -4158,6 +4511,7 @@ version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -4166,7 +4520,7 @@ files = [ [package.extras] develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] docs = ["sphinx"] -gmpy = ["gmpy2 (>=2.1.0a4)"] +gmpy = ["gmpy2 (>=2.1.0a4) ; platform_python_implementation != \"PyPy\""] tests = ["pytest (>=4.6)"] [[package]] @@ -4175,6 +4529,8 @@ version = "1.1.1" description = "MessagePack serializer" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed"}, {file = "msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8"}, @@ -4243,6 +4599,7 @@ version = "6.6.3" description = "multidict implementation" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "multidict-6.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2be5b7b35271f7fff1397204ba6708365e3d773579fe2a30625e16c4b4ce817"}, {file = "multidict-6.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12f4581d2930840295c461764b9a65732ec01250b46c6b2c510d7ee68872b140"}, @@ -4365,6 +4722,7 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -4376,6 +4734,7 @@ version = "1.6.0" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" +groups = ["main", "dev"] files = [ {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, @@ -4387,6 +4746,7 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -4406,6 +4766,7 @@ version = "3.9.1" description = "Natural Language Toolkit" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, @@ -4431,6 +4792,8 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\" or extra == \"desktop\"" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -4442,6 +4805,7 @@ version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, @@ -4506,6 +4870,7 @@ version = "1.22.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = ">=3.10" +groups = ["main"] files = [ {file = "onnxruntime-1.22.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:80e7f51da1f5201c1379b8d6ef6170505cd800e40da216290f5e06be01aadf95"}, {file = "onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89ddfdbbdaf7e3a59515dee657f6515601d55cb21a0f0f48c81aefc54ff1b73"}, @@ -4541,6 +4906,7 @@ version = "1.93.3" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "openai-1.93.3-py3-none-any.whl", hash = "sha256:41aaa7594c7d141b46eed0a58dcd75d20edcc809fdd2c931ecbb4957dc98a892"}, {file = "openai-1.93.3.tar.gz", hash = "sha256:488b76399238c694af7e4e30c58170ea55e6f65038ab27dbe95b5077a00f8af8"}, @@ -4568,6 +4934,7 @@ version = "1.30.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09"}, {file = "opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240"}, @@ -4583,6 +4950,7 @@ version = "1.30.0" description = "OpenTelemetry Collector Exporters" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp-1.30.0-py3-none-any.whl", hash = "sha256:44e11054ec571ccfed73a83c6429dee5d334d061d0e0572e3160d6de97156dbc"}, {file = "opentelemetry_exporter_otlp-1.30.0.tar.gz", hash = "sha256:da7769f95cd5be5b09dd4188ac153a33709eda652217f2d10aed6518c8e60f0d"}, @@ -4598,6 +4966,7 @@ version = "1.30.0" description = "OpenTelemetry Protobuf encoding" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_common-1.30.0-py3-none-any.whl", hash = "sha256:5468007c81aa9c44dc961ab2cf368a29d3475977df83b4e30aeed42aa7bc3b38"}, {file = "opentelemetry_exporter_otlp_proto_common-1.30.0.tar.gz", hash = "sha256:ddbfbf797e518411857d0ca062c957080279320d6235a279f7b64ced73c13897"}, @@ -4612,6 +4981,7 @@ version = "1.30.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0-py3-none-any.whl", hash = "sha256:2906bcae3d80acc54fd1ffcb9e44d324e8631058b502ebe4643ca71d1ff30830"}, {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0.tar.gz", hash = "sha256:d0f10f0b9b9a383b7d04a144d01cb280e70362cccc613987e234183fd1f01177"}, @@ -4632,6 +5002,7 @@ version = "1.30.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_exporter_otlp_proto_http-1.30.0-py3-none-any.whl", hash = "sha256:9578e790e579931c5ffd50f1e6975cbdefb6a0a0a5dea127a6ae87df10e0a589"}, {file = "opentelemetry_exporter_otlp_proto_http-1.30.0.tar.gz", hash = "sha256:c3ae75d4181b1e34a60662a6814d0b94dd33b628bee5588a878bed92cee6abdc"}, @@ -4652,6 +5023,7 @@ version = "0.51b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_instrumentation-0.51b0-py3-none-any.whl", hash = "sha256:c6de8bd26b75ec8b0e54dff59e198946e29de6a10ec65488c357d4b34aa5bdcf"}, {file = "opentelemetry_instrumentation-0.51b0.tar.gz", hash = "sha256:4ca266875e02f3988536982467f7ef8c32a38b8895490ddce9ad9604649424fa"}, @@ -4669,6 +5041,7 @@ version = "0.51b0" description = "OpenTelemetry requests instrumentation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_instrumentation_requests-0.51b0-py3-none-any.whl", hash = "sha256:0723aaafaeb2a825723f31c0bf644f9642377046063d1a52fc86571ced87feac"}, {file = "opentelemetry_instrumentation_requests-0.51b0.tar.gz", hash = "sha256:e7f4bd3ffcab6ebcce8a1c652af218e050354c8e7cac2c34814292d4de75167a"}, @@ -4689,6 +5062,7 @@ version = "0.51b0" description = "OpenTelemetry SQLAlchemy instrumentation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_instrumentation_sqlalchemy-0.51b0-py3-none-any.whl", hash = "sha256:5ff4816228b496aef1511149e2b17a25e0faacec4d5eb65bf18a9964af40f1af"}, {file = "opentelemetry_instrumentation_sqlalchemy-0.51b0.tar.gz", hash = "sha256:dbfe95b69006017f903dda194606be458d54789e6b3419d37161fb8861bb98a5"}, @@ -4710,6 +5084,7 @@ version = "1.30.0" description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_proto-1.30.0-py3-none-any.whl", hash = "sha256:c6290958ff3ddacc826ca5abbeb377a31c2334387352a259ba0df37c243adc11"}, {file = "opentelemetry_proto-1.30.0.tar.gz", hash = "sha256:afe5c9c15e8b68d7c469596e5b32e8fc085eb9febdd6fb4e20924a93a0389179"}, @@ -4724,6 +5099,7 @@ version = "1.30.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091"}, {file = "opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18"}, @@ -4740,6 +5116,7 @@ version = "0.51b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae"}, {file = "opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47"}, @@ -4755,6 +5132,7 @@ version = "0.51b0" description = "Web util for OpenTelemetry" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_util_http-0.51b0-py3-none-any.whl", hash = "sha256:0561d7a6e9c422b9ef9ae6e77eafcfcd32a2ab689f5e801475cbb67f189efa20"}, {file = "opentelemetry_util_http-0.51b0.tar.gz", hash = "sha256:05edd19ca1cc3be3968b1e502fd94816901a365adbeaab6b6ddb974384d3a0b9"}, @@ -4766,6 +5144,8 @@ version = "3.10.18" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\" and (extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\")" files = [ {file = "orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402"}, {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c"}, @@ -4847,6 +5227,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "dev,tests"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -4858,6 +5239,7 @@ version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -4905,9 +5287,9 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -4944,6 +5326,7 @@ version = "3.5.1" description = "SSH2 protocol library" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61"}, {file = "paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822"}, @@ -4955,8 +5338,8 @@ cryptography = ">=3.3" pynacl = ">=1.5" [package.extras] -all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] -gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +all = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] +gssapi = ["gssapi (>=1.4.1) ; platform_system != \"Windows\"", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8) ; platform_system == \"Windows\""] invoke = ["invoke (>=2.0)"] [[package]] @@ -4965,10 +5348,12 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] @@ -4980,6 +5365,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -4991,6 +5377,7 @@ version = "3.3.1" description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f"}, {file = "pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177"}, @@ -5007,6 +5394,7 @@ version = "20250506" description = "PDF parser and analyzer" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pdfminer_six-20250506-py3-none-any.whl", hash = "sha256:d81ad173f62e5f841b53a8ba63af1a4a355933cfc0ffabd608e568b9193909e3"}, {file = "pdfminer_six-20250506.tar.gz", hash = "sha256:b03cc8df09cf3c7aba8246deae52e0bca7ebb112a38895b5e1d4f5dd2b8ca2e7"}, @@ -5017,7 +5405,7 @@ charset-normalizer = ">=2.0.0" cryptography = ">=36.0.0" [package.extras] -dev = ["atheris", "black", "mypy (==0.931)", "nox", "pytest"] +dev = ["atheris ; python_version < \"3.12\"", "black", "mypy (==0.931)", "nox", "pytest"] docs = ["sphinx", "sphinx-argparse"] image = ["Pillow"] @@ -5027,10 +5415,12 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\"", dev = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or extra == \"dev\" or extra == \"all\""} [package.dependencies] ptyprocess = ">=0.5" @@ -5041,6 +5431,8 @@ version = "1.31.2" description = "PostgreSQL interface library" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6"}, {file = "pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876"}, @@ -5056,6 +5448,8 @@ version = "0.2.5" description = "pgvector support for Python" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "pgvector-0.2.5-py2.py3-none-any.whl", hash = "sha256:5e5e93ec4d3c45ab1fa388729d56c602f6966296e19deee8878928c6d567e41b"}, ] @@ -5069,6 +5463,7 @@ version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, @@ -5157,7 +5552,7 @@ docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] +typing = ["typing-extensions ; python_version < \"3.10\""] xmp = ["defusedxml"] [[package]] @@ -5166,6 +5561,8 @@ version = "7.3.0" description = "Pinecone client and SDK" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "pinecone-7.3.0-py3-none-any.whl", hash = "sha256:315b8fef20320bef723ecbb695dec0aafa75d8434d86e01e5a0e85933e1009a8"}, {file = "pinecone-7.3.0.tar.gz", hash = "sha256:307edc155621d487c20dc71b76c3ad5d6f799569ba42064190d03917954f9a7b"}, @@ -5180,13 +5577,13 @@ pinecone-plugin-interface = ">=0.0.7,<0.0.8" python-dateutil = ">=2.5.3" typing-extensions = ">=3.7.4" urllib3 = [ - {version = ">=1.26.0", markers = "python_version >= \"3.8\" and python_version < \"3.12\""}, {version = ">=1.26.5", markers = "python_version >= \"3.12\" and python_version < \"4.0\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.8\" and python_version < \"3.12\""}, ] [package.extras] asyncio = ["aiohttp (>=3.9.0)", "aiohttp-retry (>=2.9.1,<3.0.0)"] -grpc = ["googleapis-common-protos (>=1.66.0)", "grpcio (>=1.44.0)", "grpcio (>=1.59.0)", "grpcio (>=1.68.0)", "lz4 (>=3.1.3)", "protobuf (>=5.29,<6.0)", "protoc-gen-openapiv2 (>=0.0.1,<0.0.2)"] +grpc = ["googleapis-common-protos (>=1.66.0)", "grpcio (>=1.44.0) ; python_version >= \"3.8\" and python_version < \"3.11\"", "grpcio (>=1.59.0) ; python_version >= \"3.11\" and python_version < \"4.0\"", "grpcio (>=1.68.0) ; python_version >= \"3.13\" and python_version < \"4.0\"", "lz4 (>=3.1.3)", "protobuf (>=5.29,<6.0)", "protoc-gen-openapiv2 (>=0.0.1,<0.0.2)"] [[package]] name = "pinecone-plugin-assistant" @@ -5194,6 +5591,8 @@ version = "1.7.0" description = "Assistant plugin for Pinecone SDK" optional = true python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "pinecone_plugin_assistant-1.7.0-py3-none-any.whl", hash = "sha256:864cb8e7930588e6c2da97c6d44f0240969195f43fa303c5db76cbc12bf903a5"}, {file = "pinecone_plugin_assistant-1.7.0.tar.gz", hash = "sha256:e26e3ba10a8b71c3da0d777cff407668022e82963c4913d0ffeb6c552721e482"}, @@ -5209,6 +5608,8 @@ version = "0.0.7" description = "Plugin interface for the Pinecone python client" optional = true python-versions = "<4.0,>=3.8" +groups = ["main"] +markers = "extra == \"pinecone\" or extra == \"all\"" files = [ {file = "pinecone_plugin_interface-0.0.7-py3-none-any.whl", hash = "sha256:875857ad9c9fc8bbc074dbe780d187a2afd21f5bfe0f3b08601924a61ef1bba8"}, {file = "pinecone_plugin_interface-0.0.7.tar.gz", hash = "sha256:b8e6675e41847333aa13923cc44daa3f85676d7157324682dc1640588a982846"}, @@ -5220,6 +5621,7 @@ version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, @@ -5236,10 +5638,12 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" +groups = ["main", "dev", "dev,tests"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] dev = ["pre-commit", "tox"] @@ -5251,6 +5655,8 @@ version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, @@ -5269,6 +5675,7 @@ version = "3.16.0" description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "prettytable-3.16.0-py3-none-any.whl", hash = "sha256:b5eccfabb82222f5aa46b798ff02a8452cf530a352c31bddfa29be41242863aa"}, {file = "prettytable-3.16.0.tar.gz", hash = "sha256:3c64b31719d961bf69c9a7e03d0c1e477320906a98da63952bc6698d6164ff57"}, @@ -5286,6 +5693,7 @@ version = "3.0.51" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, @@ -5300,6 +5708,7 @@ version = "0.3.2" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, @@ -5401,12 +5810,32 @@ files = [ {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"}, ] +[[package]] +name = "proto-plus" +version = "1.26.1" +description = "Beautiful, Pythonic protocol buffers" +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"}, + {file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<7.0.0" + +[package.extras] +testing = ["google-api-core (>=1.31.5)"] + [[package]] name = "protobuf" version = "5.29.5" description = "" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079"}, {file = "protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc"}, @@ -5427,6 +5856,7 @@ version = "7.0.0" description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, @@ -5439,6 +5869,7 @@ files = [ {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, ] +markers = {main = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\""} [package.extras] dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] @@ -5450,6 +5881,8 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, {file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"}, @@ -5457,6 +5890,7 @@ files = [ {file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"}, {file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"}, {file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"}, + {file = "psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2"}, {file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"}, {file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"}, {file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"}, @@ -5468,6 +5902,8 @@ version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, @@ -5516,6 +5952,7 @@ files = [ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, @@ -5544,10 +5981,12 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\"", dev = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or extra == \"dev\" or extra == \"all\""} [[package]] name = "pure-eval" @@ -5555,10 +5994,12 @@ version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] tests = ["pytest"] @@ -5569,6 +6010,8 @@ version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"google\" or extra == \"experimental\" or extra == \"all\"" files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -5580,6 +6023,8 @@ version = "0.4.2" description = "A collection of ASN.1-based protocols modules" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"google\" or extra == \"experimental\" or extra == \"all\"" files = [ {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, @@ -5594,10 +6039,12 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +markers = {dev = "implementation_name == \"pypy\""} [[package]] name = "pydantic" @@ -5605,6 +6052,7 @@ version = "2.11.7" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, @@ -5619,7 +6067,7 @@ typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" @@ -5627,6 +6075,7 @@ version = "2.33.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, @@ -5738,6 +6187,7 @@ version = "2.10.1" description = "Settings management using Pydantic" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796"}, {file = "pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee"}, @@ -5761,6 +6211,8 @@ version = "3.4.0" description = "passive checker of Python programs" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, @@ -5772,6 +6224,7 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main", "dev", "dev,tests"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -5786,6 +6239,7 @@ version = "3.8.0" description = "🐫 Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"}, {file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"}, @@ -5797,6 +6251,7 @@ version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, @@ -5823,6 +6278,7 @@ version = "3.2.3" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, @@ -5837,6 +6293,7 @@ version = "5.7.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pypdf-5.7.0-py3-none-any.whl", hash = "sha256:203379453439f5b68b7a1cd43cdf4c5f7a02b84810cefa7f93a47b350aaaba48"}, {file = "pypdf-5.7.0.tar.gz", hash = "sha256:68c92f2e1aae878bab1150e74447f31ab3848b1c0a6f8becae9f0b1904460b6f"}, @@ -5859,6 +6316,7 @@ version = "1.9.0" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, ] @@ -5869,6 +6327,8 @@ version = "3.5.4" description = "A python implementation of GNU readline." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, @@ -5883,6 +6343,8 @@ version = "1.1.403" description = "Command line wrapper for pyright" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3"}, {file = "pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104"}, @@ -5903,6 +6365,7 @@ version = "1.0.8" description = "Pusher websocket client for python, based on Erik Kulyk's PythonPusherClient" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "Pysher-1.0.8.tar.gz", hash = "sha256:7849c56032b208e49df67d7bd8d49029a69042ab0bb45b2ed59fa08f11ac5988"}, ] @@ -5917,10 +6380,12 @@ version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" +groups = ["main", "dev", "dev,tests"] files = [ {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} @@ -5940,6 +6405,8 @@ version = "0.24.0" description = "Pytest support for asyncio" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, @@ -5958,6 +6425,7 @@ version = "1.5.0" description = "A pytest plugin to report test results as JSON files" optional = false python-versions = "*" +groups = ["dev,tests"] files = [ {file = "pytest-json-report-1.5.0.tar.gz", hash = "sha256:2dde3c647851a19b5f3700729e8310a6e66efb2077d674f27ddea3d34dc615de"}, {file = "pytest_json_report-1.5.0-py3-none-any.whl", hash = "sha256:9897b68c910b12a2e48dd849f9a284b2c79a732a8a9cb398452ddd23d3c8c325"}, @@ -5973,6 +6441,7 @@ version = "3.1.1" description = "pytest plugin for test session metadata" optional = false python-versions = ">=3.8" +groups = ["dev,tests"] files = [ {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, @@ -5990,6 +6459,7 @@ version = "3.14.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, @@ -6007,6 +6477,8 @@ version = "1.3.0" description = "pytest plugin to run your tests in a specific order" optional = true python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "pytest_order-1.3.0-py3-none-any.whl", hash = "sha256:2cd562a21380345dd8d5774aa5fd38b7849b6ee7397ca5f6999bbe6e89f07f6e"}, {file = "pytest_order-1.3.0.tar.gz", hash = "sha256:51608fec3d3ee9c0adaea94daa124a5c4c1d2bb99b00269f098f414307f23dde"}, @@ -6021,6 +6493,7 @@ version = "7.3.2" description = "Advanced Python dictionaries with dot notation access" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "python_box-7.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d136163294fd61a1554db7dd203f2e3035064798d30c17d67d948f0de5c572de"}, {file = "python_box-7.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d72e96547d8e2c2c333909826e9fae338d9a7e4cde07d5c6058cdd468432c0"}, @@ -6047,7 +6520,7 @@ msgpack = ["msgpack"] pyyaml = ["PyYAML"] ruamel-yaml = ["ruamel.yaml (>=0.17)"] toml = ["toml"] -tomli = ["tomli", "tomli-w"] +tomli = ["tomli ; python_version < \"3.11\"", "tomli-w"] yaml = ["ruamel.yaml (>=0.17)"] [[package]] @@ -6056,6 +6529,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -6070,6 +6544,7 @@ version = "1.1.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"}, {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"}, @@ -6084,6 +6559,8 @@ version = "4.12.2" description = "Engine.IO server and client for Python" optional = true python-versions = ">=3.6" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "python_engineio-4.12.2-py3-none-any.whl", hash = "sha256:8218ab66950e179dfec4b4bbb30aecf3f5d86f5e58e6fc1aa7fde2c698b2804f"}, {file = "python_engineio-4.12.2.tar.gz", hash = "sha256:e7e712ffe1be1f6a05ee5f951e72d434854a32fcfc7f6e4d9d3cae24ec70defa"}, @@ -6103,6 +6580,7 @@ version = "0.0.19" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"}, {file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"}, @@ -6114,6 +6592,7 @@ version = "1.0.2" description = "Create, read, and update PowerPoint 2007+ (.pptx) files." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba"}, {file = "python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095"}, @@ -6131,6 +6610,8 @@ version = "5.13.0" description = "Socket.IO server and client for Python" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "python_socketio-5.13.0-py3-none-any.whl", hash = "sha256:51f68d6499f2df8524668c24bcec13ba1414117cfb3a90115c559b601ab10caf"}, {file = "python_socketio-5.13.0.tar.gz", hash = "sha256:ac4e19a0302ae812e23b712ec8b6427ca0521f7c582d6abb096e36e24a263029"}, @@ -6153,6 +6634,7 @@ version = "2023.4" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytz-2023.4-py2.py3-none-any.whl", hash = "sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a"}, {file = "pytz-2023.4.tar.gz", hash = "sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40"}, @@ -6164,6 +6646,7 @@ version = "310" description = "Python for Window Extensions" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, {file = "pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d"}, @@ -6182,6 +6665,7 @@ files = [ {file = "pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a"}, {file = "pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475"}, ] +markers = {main = "sys_platform == \"win32\" and (extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\" or extra == \"dev\")", dev = "platform_python_implementation != \"PyPy\" and sys_platform == \"win32\""} [[package]] name = "pyyaml" @@ -6189,6 +6673,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -6251,6 +6736,7 @@ version = "27.0.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pyzmq-27.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b973ee650e8f442ce482c1d99ca7ab537c69098d53a3d046676a484fd710c87a"}, {file = "pyzmq-27.0.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:661942bc7cd0223d569d808f2e5696d9cc120acc73bf3e88a1f1be7ab648a7e4"}, @@ -6332,6 +6818,7 @@ files = [ {file = "pyzmq-27.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:096af9e133fec3a72108ddefba1e42985cb3639e9de52cfd336b6fc23aa083e9"}, {file = "pyzmq-27.0.0.tar.gz", hash = "sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf"}, ] +markers = {main = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\""} [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} @@ -6342,6 +6829,7 @@ version = "2.1.0" description = "Python library to build pretty command line user prompts ⭐️" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, @@ -6356,6 +6844,8 @@ version = "6.2.0" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"redis\" or extra == \"all\"" files = [ {file = "redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e"}, {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, @@ -6375,6 +6865,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -6391,6 +6882,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -6494,6 +6986,7 @@ version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, @@ -6515,6 +7008,8 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -6529,6 +7024,7 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -6548,6 +7044,7 @@ version = "0.26.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37"}, {file = "rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0"}, @@ -6701,6 +7198,8 @@ version = "4.9.1" description = "Pure-Python RSA implementation" optional = true python-versions = "<4,>=3.6" +groups = ["main"] +markers = "extra == \"google\" or extra == \"experimental\" or extra == \"all\"" files = [ {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"}, {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"}, @@ -6715,6 +7214,8 @@ version = "0.11.3" description = "An Amazon S3 Transfer Manager" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"bedrock\"" files = [ {file = "s3transfer-0.11.3-py3-none-any.whl", hash = "sha256:ca855bdeb885174b5ffa95b9913622459d4ad8e331fc98eb01e6d5eb6a30655d"}, {file = "s3transfer-0.11.3.tar.gz", hash = "sha256:edae4977e3a122445660c7c114bba949f9d191bae3b34a096f18a1c8c354527a"}, @@ -6732,6 +7233,8 @@ version = "1.4.6" description = "An implementation of the SCRAM protocol." optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"postgres\" or extra == \"all\"" files = [ {file = "scramp-1.4.6-py3-none-any.whl", hash = "sha256:a0cf9d2b4624b69bac5432dd69fecfc55a542384fe73c3a23ed9b138cda484e1"}, {file = "scramp-1.4.6.tar.gz", hash = "sha256:fe055ebbebf4397b9cb323fcc4b299f219cd1b03fd673ca40c97db04ac7d107e"}, @@ -6746,6 +7249,7 @@ version = "3.0.4" description = "Python helper for Semantic Versioning (https://semver.org)" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, @@ -6757,6 +7261,7 @@ version = "2.19.1" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "sentry_sdk-2.19.1-py2.py3-none-any.whl", hash = "sha256:b056e04b766f805fdf0aa620482cafe2ff000c8fcb51cb266cdb90873e93837b"}, {file = "sentry_sdk-2.19.1.tar.gz", hash = "sha256:6ad8507457a379b72f832aca55787b21e7391751892faef1fd8bace350aa5e17"}, @@ -6812,6 +7317,7 @@ version = "70.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc"}, {file = "setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5"}, @@ -6819,7 +7325,7 @@ files = [ [package.extras] 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.10.0)", "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.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "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 ; sys_platform != \"cygwin\"", "pytest-ruff (>=0.3.2) ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" @@ -6827,6 +7333,7 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -6838,6 +7345,8 @@ version = "1.1.0" description = "Simple WebSocket server and client for Python" optional = true python-versions = ">=3.6" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c"}, {file = "simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"}, @@ -6856,6 +7365,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -6867,6 +7377,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -6878,6 +7389,7 @@ version = "2.7" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, @@ -6889,6 +7401,7 @@ version = "2.0.41" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, @@ -6984,6 +7497,7 @@ version = "0.7.0" description = "JSON type with nested change tracking for SQLAlchemy" optional = false python-versions = ">= 3.6" +groups = ["main"] files = [ {file = "sqlalchemy-json-0.7.0.tar.gz", hash = "sha256:620d0b26f648f21a8fa9127df66f55f83a5ab4ae010e5397a5c6989a08238561"}, {file = "sqlalchemy_json-0.7.0-py3-none-any.whl", hash = "sha256:27881d662ca18363a4ac28175cc47ea2a6f2bef997ae1159c151026b741818e6"}, @@ -7001,6 +7515,7 @@ version = "0.41.2" description = "Various utility functions for SQLAlchemy." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "SQLAlchemy-Utils-0.41.2.tar.gz", hash = "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990"}, {file = "SQLAlchemy_Utils-0.41.2-py3-none-any.whl", hash = "sha256:85cf3842da2bf060760f955f8467b87983fb2e30f1764fd0e24a48307dc8ec6e"}, @@ -7018,8 +7533,8 @@ intervals = ["intervals (>=0.7.1)"] password = ["passlib (>=1.6,<2.0)"] pendulum = ["pendulum (>=2.0.5)"] phone = ["phonenumbers (>=5.9.2)"] -test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] -test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo ; python_version < \"3.9\"", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo ; python_version < \"3.9\"", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (==7.4.4)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] timezone = ["python-dateutil"] url = ["furl (>=0.4.1)"] @@ -7029,6 +7544,7 @@ version = "0.1.7a2" description = "" optional = false python-versions = "*" +groups = ["sqlite"] files = [ {file = "sqlite_vec-0.1.7a2-py3-none-macosx_10_6_x86_64.whl", hash = "sha256:a08dd9396d494ac8970ba519a3931410f08c0c5eeadd0e1a2e02053789f6c877"}, {file = "sqlite_vec-0.1.7a2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b98d7af645d28c0b5c844bf1d99fe2103fe1320fe2bbf36d0713f0b36764fdcb"}, @@ -7043,6 +7559,7 @@ version = "0.0.16" description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." optional = false python-versions = ">=3.7,<4.0" +groups = ["main"] files = [ {file = "sqlmodel-0.0.16-py3-none-any.whl", hash = "sha256:b972f5d319580d6c37ecc417881f6ec4d1ad3ed3583d0ac0ed43234a28bf605a"}, {file = "sqlmodel-0.0.16.tar.gz", hash = "sha256:966656f18a8e9a2d159eb215b07fb0cf5222acfae3362707ca611848a8a06bd1"}, @@ -7058,6 +7575,7 @@ version = "2.4.1" description = "SSE plugin for Starlette" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "sse_starlette-2.4.1-py3-none-any.whl", hash = "sha256:08b77ea898ab1a13a428b2b6f73cfe6d0e607a7b4e15b9bb23e4a37b087fd39a"}, {file = "sse_starlette-2.4.1.tar.gz", hash = "sha256:7c8a800a1ca343e9165fc06bbda45c78e4c6166320707ae30b416c42da070926"}, @@ -7078,10 +7596,12 @@ version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.dependencies] asttokens = ">=2.1.0" @@ -7097,6 +7617,7 @@ version = "0.46.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, @@ -7114,6 +7635,7 @@ version = "0.0.26" description = "A simple library to convert rtf to text" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb"}, {file = "striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa"}, @@ -7125,6 +7647,7 @@ version = "25.4.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, @@ -7139,6 +7662,7 @@ version = "1.14.0" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"}, {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"}, @@ -7156,6 +7680,7 @@ version = "0.7.9" description = "Python wrapper for the Tavily API" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "tavily_python-0.7.9-py3-none-any.whl", hash = "sha256:6d70ea86e2ccba061d0ea98c81922784a01c186960304d44436304f114f22372"}, {file = "tavily_python-0.7.9.tar.gz", hash = "sha256:61aa13ca89e2e40d645042c8d27afc478b27846fb79bb21d4f683ed28f173dc7"}, @@ -7172,6 +7697,7 @@ version = "8.5.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, @@ -7187,6 +7713,7 @@ version = "0.9.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, @@ -7234,6 +7761,8 @@ version = "6.2.0" description = "A wrapper around the stdlib `tokenize` which roundtrips." optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "tokenize_rt-6.2.0-py2.py3-none-any.whl", hash = "sha256:a152bf4f249c847a66497a4a95f63376ed68ac6abf092a2f7cfb29d044ecff44"}, {file = "tokenize_rt-6.2.0.tar.gz", hash = "sha256:8439c042b330c553fdbe1758e4a05c0ed460dbbbb24a606f11f0dee75da4cad6"}, @@ -7245,6 +7774,8 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] +markers = "python_version == \"3.10\"" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -7256,6 +7787,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["main", "dev", "dev,tests"] +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -7297,6 +7830,7 @@ version = "6.5.1" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7"}, {file = "tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6"}, @@ -7318,6 +7852,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -7339,10 +7874,12 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, ] +markers = {main = "extra == \"dev\" or extra == \"all\""} [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] @@ -7354,6 +7891,7 @@ version = "0.15.4" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "typer-0.15.4-py3-none-any.whl", hash = "sha256:eb0651654dcdea706780c466cf06d8f174405a659ffff8f163cfbfee98c0e173"}, {file = "typer-0.15.4.tar.gz", hash = "sha256:89507b104f9b6a0730354f27c39fae5b63ccd0c95b1ce1f1a6ba0cfd329997c3"}, @@ -7371,10 +7909,12 @@ version = "4.14.1" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" +groups = ["main", "dev", "dev,tests", "sqlite"] files = [ {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, ] +markers = {dev = "python_version < \"3.12\"", "dev,tests" = "python_version == \"3.10\""} [[package]] name = "typing-inspect" @@ -7382,6 +7922,7 @@ version = "0.9.0" description = "Runtime inspection utilities for typing module." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, @@ -7397,6 +7938,7 @@ version = "0.4.1" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, @@ -7411,6 +7953,7 @@ version = "2025.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main"] files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, @@ -7422,6 +7965,7 @@ version = "5.3.1" description = "tzinfo object for the local timezone" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d"}, {file = "tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd"}, @@ -7433,19 +7977,33 @@ tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] +[[package]] +name = "uritemplate" +version = "4.2.0" +description = "Implementation of RFC 6570 URI Templates" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" +files = [ + {file = "uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686"}, + {file = "uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e"}, +] + [[package]] name = "urllib3" version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -7456,6 +8014,7 @@ version = "0.24.0.post1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, @@ -7467,7 +8026,7 @@ h11 = ">=0.8" typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "uvloop" @@ -7475,6 +8034,8 @@ version = "0.21.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = true python-versions = ">=3.8.0" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, @@ -7526,6 +8087,8 @@ version = "20.31.2" description = "Virtual Python Environment builder" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"all\"" files = [ {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"}, {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"}, @@ -7538,7 +8101,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "watchfiles" @@ -7546,6 +8109,8 @@ version = "1.1.0" description = "Simple, modern and high performance file watching and code reload in python." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"experimental\" or extra == \"all\"" files = [ {file = "watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc"}, {file = "watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df"}, @@ -7664,6 +8229,7 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -7675,6 +8241,7 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" +groups = ["main"] 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"}, @@ -7691,6 +8258,8 @@ version = "15.0.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"google\" or extra == \"external-tools\"" files = [ {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b"}, {file = "websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205"}, @@ -7769,6 +8338,8 @@ version = "3.1.3" description = "The comprehensive WSGI web application library." optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -7786,6 +8357,8 @@ version = "1.4.0" description = "Wikipedia API for Python" optional = true python-versions = "*" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"tests\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "wikipedia-1.4.0.tar.gz", hash = "sha256:db0fad1829fdd441b1852306e9856398204dc0786d2996dd2e0c8bb8e26133b2"}, ] @@ -7800,6 +8373,7 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -7888,6 +8462,8 @@ version = "1.2.0" description = "WebSockets state-machine based protocol implementation" optional = true python-versions = ">=3.7.0" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, @@ -7902,6 +8478,7 @@ version = "3.2.5" description = "A Python module for creating Excel XLSX files." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "xlsxwriter-3.2.5-py3-none-any.whl", hash = "sha256:4f4824234e1eaf9d95df9a8fe974585ff91d0f5e3d3f12ace5b71e443c1c6abd"}, {file = "xlsxwriter-3.2.5.tar.gz", hash = "sha256:7e88469d607cdc920151c0ab3ce9cf1a83992d4b7bc730c5ffdd1a12115a7dbe"}, @@ -7913,6 +8490,7 @@ version = "1.20.1" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, @@ -8031,13 +8609,14 @@ version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -8050,6 +8629,8 @@ version = "5.1" description = "Very basic event publishing system" optional = true python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zope_event-5.1-py3-none-any.whl", hash = "sha256:53de8f0e9f61dc0598141ac591f49b042b6d74784dab49971b9cc91d0f73a7df"}, {file = "zope_event-5.1.tar.gz", hash = "sha256:a153660e0c228124655748e990396b9d8295d6e4f546fa1b34f3319e1c666e7f"}, @@ -8068,6 +8649,8 @@ version = "7.2" description = "Interfaces for Python" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -8122,6 +8705,8 @@ version = "0.23.0" description = "Zstandard bindings for Python" optional = true python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"external-tools\" or extra == \"desktop\" or extra == \"all\"" files = [ {file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"}, {file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"}, @@ -8229,12 +8814,12 @@ cffi = {version = ">=1.11", markers = "platform_python_implementation == \"PyPy\ cffi = ["cffi (>=1.11)"] [extras] -all = ["autoflake", "black", "docker", "fastapi", "granian", "isort", "langchain", "langchain-community", "locust", "pexpect", "pg8000", "pgvector", "pinecone", "pre-commit", "psycopg2", "psycopg2-binary", "pyright", "pytest-asyncio", "pytest-order", "redis", "uvicorn", "uvloop", "wikipedia"] +all = ["autoflake", "black", "docker", "fastapi", "google-cloud-profiler", "granian", "isort", "langchain", "langchain-community", "locust", "pexpect", "pg8000", "pgvector", "pinecone", "pre-commit", "psycopg2", "psycopg2-binary", "pyright", "pytest-asyncio", "pytest-order", "redis", "uvicorn", "uvloop", "wikipedia"] bedrock = ["aioboto3", "boto3"] cloud-tool-sandbox = ["e2b-code-interpreter"] desktop = ["docker", "fastapi", "langchain", "langchain-community", "locust", "pyright", "uvicorn", "wikipedia"] dev = ["autoflake", "black", "isort", "locust", "pexpect", "pre-commit", "pyright", "pytest-asyncio", "pytest-order"] -experimental = ["granian", "uvloop"] +experimental = ["google-cloud-profiler", "granian", "uvloop"] external-tools = ["docker", "firecrawl-py", "langchain", "langchain-community", "wikipedia"] google = ["google-genai"] pinecone = ["pinecone"] @@ -8244,6 +8829,6 @@ server = ["fastapi", "uvicorn"] tests = ["wikipedia"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "<3.14,>=3.10" -content-hash = "f7c0b095785d1e7363377731f8732075bd8ef01280490836bf651bb0587494b4" +content-hash = "aed0b8f2e1904d30a694331d6e2c428a097433ed2169253426b38580826b8fc7" diff --git a/pyproject.toml b/pyproject.toml index eecda66f..f36af1aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,7 @@ certifi = "^2025.6.15" aioboto3 = {version = "^14.3.0", optional = true} pinecone = {extras = ["asyncio"], version = "^7.3.0", optional = true} markitdown = {extras = ["docx", "pdf", "pptx"], version = "^0.1.2"} +google-cloud-profiler = {version = "^4.1.0", optional = true} [tool.poetry.extras] @@ -108,7 +109,7 @@ postgres = ["pgvector", "pg8000", "psycopg2-binary", "psycopg2", "asyncpg"] redis = ["redis"] pinecone = ["pinecone"] dev = ["pytest", "pytest-asyncio", "pexpect", "black", "pre-commit", "pyright", "pytest-order", "autoflake", "isort", "locust"] -experimental = ["uvloop", "granian"] +experimental = ["uvloop", "granian", "google-cloud-profiler"] server = ["websockets", "fastapi", "uvicorn"] cloud-tool-sandbox = ["e2b-code-interpreter"] external-tools = ["docker", "langchain", "wikipedia", "langchain-community", "firecrawl-py"] @@ -116,7 +117,7 @@ tests = ["wikipedia"] bedrock = ["boto3", "aioboto3"] google = ["google-genai"] desktop = ["pyright", "websockets", "fastapi", "uvicorn", "docker", "langchain", "wikipedia", "langchain-community", "locust"] -all = ["pgvector", "pg8000", "psycopg2-binary", "psycopg2", "pytest", "pytest-asyncio", "pexpect", "black", "pre-commit", "pyright", "pytest-order", "autoflake", "isort", "websockets", "fastapi", "uvicorn", "docker", "langchain", "wikipedia", "langchain-community", "locust", "uvloop", "granian", "redis", "pinecone"] +all = ["pgvector", "pg8000", "psycopg2-binary", "psycopg2", "pytest", "pytest-asyncio", "pexpect", "black", "pre-commit", "pyright", "pytest-order", "autoflake", "isort", "websockets", "fastapi", "uvicorn", "docker", "langchain", "wikipedia", "langchain-community", "locust", "uvloop", "granian", "redis", "pinecone", "google-cloud-profiler"] [tool.poetry.group.dev.dependencies] black = "^24.4.2" From 478972358ee29575f55893a6f775d2ae189ee813 Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Fri, 25 Jul 2025 13:46:12 -0700 Subject: [PATCH 19/24] chore: remove backfill --- ..._support_for_project_id_for_blocks_and_.py | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py b/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py index a2c0e062..8dab61ac 100644 --- a/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py +++ b/alembic/versions/06fbbf65d4f1_support_for_project_id_for_blocks_and_.py @@ -9,7 +9,6 @@ Create Date: 2025-07-21 15:07:32.133538 from typing import Sequence, Union import sqlalchemy as sa -from sqlalchemy import text from alembic import op @@ -25,40 +24,43 @@ def upgrade() -> None: op.add_column("block", sa.Column("project_id", sa.String(), nullable=True)) op.add_column("groups", sa.Column("project_id", sa.String(), nullable=True)) + # NOTE: running the backfill on alembic will result in locking with running application. + # This is okay if okay with downtime. Options also to do rolling migration or dynamic updates. + # Backfill project_id for blocks table # Since all agents for a block have the same project_id, we can just grab the first one - op.execute( - text( - """ - UPDATE block - SET project_id = ( - SELECT a.project_id - FROM blocks_agents ba - JOIN agents a ON ba.agent_id = a.id - WHERE ba.block_id = block.id - AND a.project_id IS NOT NULL - LIMIT 1 - ) - """ - ) - ) + # op.execute( + # text( + # """ + # UPDATE block + # SET project_id = ( + # SELECT a.project_id + # FROM blocks_agents ba + # JOIN agents a ON ba.agent_id = a.id + # WHERE ba.block_id = block.id + # AND a.project_id IS NOT NULL + # LIMIT 1 + # ) + # """ + # ) + # ) # Backfill project_id for groups table - op.execute( - text( - """ - UPDATE groups - SET project_id = ( - SELECT a.project_id - FROM groups_agents ga - JOIN agents a ON ga.agent_id = a.id - WHERE ga.group_id = groups.id - AND a.project_id IS NOT NULL - LIMIT 1 - ) - """ - ) - ) + # op.execute( + # text( + # """ + # UPDATE groups + # SET project_id = ( + # SELECT a.project_id + # FROM groups_agents ga + # JOIN agents a ON ga.agent_id = a.id + # WHERE ga.group_id = groups.id + # AND a.project_id IS NOT NULL + # LIMIT 1 + # ) + # """ + # ) + # ) # ### end Alembic commands ### From 071e8e4b5ee240b3a304245226b60a91bc7c5809 Mon Sep 17 00:00:00 2001 From: Andy Li <55300002+cliandy@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:46:19 -0700 Subject: [PATCH 20/24] fix: catch import error --- letta/server/rest_api/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letta/server/rest_api/app.py b/letta/server/rest_api/app.py index 6cc64dd3..c12b722b 100644 --- a/letta/server/rest_api/app.py +++ b/letta/server/rest_api/app.py @@ -109,7 +109,7 @@ async def lifespan(app_: FastAPI): verbose=3, ) logger.info("Profiler started.") - except (ValueError, NotImplementedError) as exc: + except Exception as exc: logger.info("Profiler not enabled: %", exc) logger.info(f"[Worker {worker_id}] Starting lifespan initialization") From 4cd1853193c50a4e9ed59213d3b78891b955f4fc Mon Sep 17 00:00:00 2001 From: jnjpng Date: Fri, 25 Jul 2025 15:19:16 -0700 Subject: [PATCH 21/24] fix: stdio form regression with streamable oauth Co-authored-by: Jin Peng --- letta/server/rest_api/routers/v1/tools.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/letta/server/rest_api/routers/v1/tools.py b/letta/server/rest_api/routers/v1/tools.py index 1165ebcd..86d50a37 100644 --- a/letta/server/rest_api/routers/v1/tools.py +++ b/letta/server/rest_api/routers/v1/tools.py @@ -43,6 +43,7 @@ from letta.services.mcp.oauth_utils import ( get_oauth_success_html, oauth_stream_event, ) +from letta.services.mcp.stdio_client import AsyncStdioMCPClient from letta.services.mcp.types import OauthStreamEvent from letta.settings import tool_settings @@ -730,8 +731,13 @@ async def connect_mcp_server( yield oauth_stream_event(OauthStreamEvent.SUCCESS, tools=tools) return except ConnectionError: + # TODO: jnjpng make this connection error check more specific to the 401 unauthorized error + if isinstance(client, AsyncStdioMCPClient): + logger.warning(f"OAuth not supported for stdio") + yield oauth_stream_event(OauthStreamEvent.ERROR, message=f"OAuth not supported for stdio") + return # Continue to OAuth flow - logger.info(f"Attempting OAuth flow for {request.server_url}...") + logger.info(f"Attempting OAuth flow for {request}...") except Exception as e: yield oauth_stream_event(OauthStreamEvent.ERROR, message=f"Connection failed: {str(e)}") return From 29bc80486daa528355cd07920cd9cab4290d828a Mon Sep 17 00:00:00 2001 From: Shubham Naik Date: Sat, 26 Jul 2025 16:14:23 -0700 Subject: [PATCH 22/24] chore: restrict custom embeddings in cloud (#3578) Co-authored-by: Shubham Naik --- tests/test_client.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 77507b0b..2f9d881d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -593,13 +593,6 @@ def test_attach_detach_agent_source(client: Letta, agent: AgentState): # Create a source source = client.sources.create( name="test_source", - embedding_config={ # TODO: change this - "embedding_endpoint": "https://embeddings.memgpt.ai", - "embedding_model": "BAAI/bge-large-en-v1.5", - "embedding_dim": 1024, - "embedding_chunk_size": 300, - "embedding_endpoint_type": "hugging-face", - }, ) initial_sources = client.agents.sources.list(agent_id=agent.id) assert source.id not in [s.id for s in initial_sources] From f77a259d07e4d88b28f7ce3caac7a50014d13522 Mon Sep 17 00:00:00 2001 From: cthomas Date: Sat, 26 Jul 2025 23:17:24 -0700 Subject: [PATCH 23/24] feat: asyncify jinja templates (#3580) --- letta/agents/base_agent.py | 2 +- letta/schemas/memory.py | 35 +++++++++++++++++++ letta/services/agent_manager.py | 16 ++++----- letta/services/tool_executor/tool_executor.py | 8 +++-- letta/services/tool_sandbox/base.py | 6 ++-- letta/services/tool_sandbox/e2b_sandbox.py | 2 +- letta/services/tool_sandbox/local_sandbox.py | 25 ++++++++----- letta/templates/template_helper.py | 26 +++++++++++++- tests/integration_test_async_tool_sandbox.py | 7 ++-- 9 files changed, 98 insertions(+), 29 deletions(-) diff --git a/letta/agents/base_agent.py b/letta/agents/base_agent.py index 1e5401b2..325090ec 100644 --- a/letta/agents/base_agent.py +++ b/letta/agents/base_agent.py @@ -122,7 +122,7 @@ class BaseAgent(ABC): curr_dynamic_section = extract_dynamic_section(curr_system_message_text) # generate just the memory string with current state for comparison - curr_memory_str = agent_state.memory.compile( + curr_memory_str = await agent_state.memory.compile_async( tool_usage_rules=tool_constraint_block, sources=agent_state.sources, max_files_open=agent_state.max_files_open ) new_dynamic_section = extract_dynamic_section(curr_memory_str) diff --git a/letta/schemas/memory.py b/letta/schemas/memory.py index c240bded..e59a6437 100644 --- a/letta/schemas/memory.py +++ b/letta/schemas/memory.py @@ -133,6 +133,25 @@ class Memory(BaseModel, validate_assignment=True): except Exception as e: raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}") + async def set_prompt_template_async(self, prompt_template: str): + """ + Async version of set_prompt_template that doesn't block the event loop. + """ + try: + # Validate Jinja2 syntax with async enabled + Template(prompt_template, enable_async=True) + + # Validate compatibility with current memory structure - use async rendering + template = Template(prompt_template, enable_async=True) + await template.render_async(blocks=self.blocks, file_blocks=self.file_blocks, sources=[], max_files_open=None) + + # If we get here, the template is valid and compatible + self.prompt_template = prompt_template + except TemplateSyntaxError as e: + raise ValueError(f"Invalid Jinja2 template syntax: {str(e)}") + except Exception as e: + raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}") + def compile(self, tool_usage_rules=None, sources=None, max_files_open=None) -> str: """Generate a string representation of the memory in-context using the Jinja2 template""" try: @@ -149,6 +168,22 @@ class Memory(BaseModel, validate_assignment=True): except Exception as e: raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}") + async def compile_async(self, tool_usage_rules=None, sources=None, max_files_open=None) -> str: + """Async version of compile that doesn't block the event loop""" + try: + template = Template(self.prompt_template, enable_async=True) + return await template.render_async( + blocks=self.blocks, + file_blocks=self.file_blocks, + tool_usage_rules=tool_usage_rules, + sources=sources, + max_files_open=max_files_open, + ) + except TemplateSyntaxError as e: + raise ValueError(f"Invalid Jinja2 template syntax: {str(e)}") + except Exception as e: + raise ValueError(f"Prompt template is not compatible with current memory structure: {str(e)}") + def list_block_labels(self) -> List[str]: """Return a list of the block names held inside the memory object""" # return list(self.memory.keys()) diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index be12684f..fed81a5b 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -1646,7 +1646,7 @@ class AgentManager: # note: we only update the system prompt if the core memory is changed # this means that the archival/recall memory statistics may be someout out of date - curr_memory_str = agent_state.memory.compile( + curr_memory_str = await agent_state.memory.compile_async( sources=agent_state.sources, tool_usage_rules=tool_rules_solver.compile_tool_rule_prompts(), max_files_open=agent_state.max_files_open, @@ -1836,14 +1836,12 @@ class AgentManager: agent_state = await self.get_agent_by_id_async(agent_id=agent_id, actor=actor, include_relationships=["memory", "sources"]) system_message = await self.message_manager.get_message_by_id_async(message_id=agent_state.message_ids[0], actor=actor) temp_tool_rules_solver = ToolRulesSolver(agent_state.tool_rules) - if ( - new_memory.compile( - sources=agent_state.sources, - tool_usage_rules=temp_tool_rules_solver.compile_tool_rule_prompts(), - max_files_open=agent_state.max_files_open, - ) - not in system_message.content[0].text - ): + new_memory_str = await new_memory.compile_async( + sources=agent_state.sources, + tool_usage_rules=temp_tool_rules_solver.compile_tool_rule_prompts(), + max_files_open=agent_state.max_files_open, + ) + if new_memory_str not in system_message.content[0].text: # update the blocks (LRW) in the DB for label in agent_state.memory.list_block_labels(): updated_value = new_memory.get_block(label).value diff --git a/letta/services/tool_executor/tool_executor.py b/letta/services/tool_executor/tool_executor.py index b57d87cb..9476e498 100644 --- a/letta/services/tool_executor/tool_executor.py +++ b/letta/services/tool_executor/tool_executor.py @@ -36,7 +36,10 @@ class SandboxToolExecutor(ToolExecutor): ) -> ToolExecutionResult: # Store original memory state - orig_memory_str = agent_state.memory.compile() if agent_state else None + if agent_state: + orig_memory_str = await agent_state.memory.compile_async() + else: + orig_memory_str = None try: # Prepare function arguments @@ -58,7 +61,8 @@ class SandboxToolExecutor(ToolExecutor): # Verify memory integrity if agent_state: - assert orig_memory_str == agent_state.memory.compile(), "Memory should not be modified in a sandbox tool" + new_memory_str = await agent_state.memory.compile_async() + assert orig_memory_str == new_memory_str, "Memory should not be modified in a sandbox tool" # Update agent memory if needed if tool_execution_result.agent_state is not None: diff --git a/letta/services/tool_sandbox/base.py b/letta/services/tool_sandbox/base.py index 03083981..68e4d109 100644 --- a/letta/services/tool_sandbox/base.py +++ b/letta/services/tool_sandbox/base.py @@ -74,12 +74,12 @@ class AsyncToolSandboxBase(ABC): """ raise NotImplementedError - def generate_execution_script(self, agent_state: Optional[AgentState], wrap_print_with_markers: bool = False) -> str: + async def generate_execution_script(self, agent_state: Optional[AgentState], wrap_print_with_markers: bool = False) -> str: """ Generate code to run inside of execution sandbox. Serialize the agent state and arguments, call the tool, then base64-encode/pickle the result. Runs a jinja2 template constructing the python file. """ - from letta.templates.template_helper import render_template + from letta.templates.template_helper import render_template_async # Select the appropriate template based on whether the function is async TEMPLATE_NAME = "sandbox_code_file_async.py.j2" if self.is_async_function else "sandbox_code_file.py.j2" @@ -106,7 +106,7 @@ class AsyncToolSandboxBase(ABC): agent_state_pickle = pickle.dumps(agent_state) if self.inject_agent_state else None - return render_template( + return await render_template_async( TEMPLATE_NAME, future_import=future_import, inject_agent_state=self.inject_agent_state, diff --git a/letta/services/tool_sandbox/e2b_sandbox.py b/letta/services/tool_sandbox/e2b_sandbox.py index ca7ca907..bdc65e5c 100644 --- a/letta/services/tool_sandbox/e2b_sandbox.py +++ b/letta/services/tool_sandbox/e2b_sandbox.py @@ -92,7 +92,7 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase): # Finally, get any that are passed explicitly into the `run` function call if additional_env_vars: env_vars.update(additional_env_vars) - code = self.generate_execution_script(agent_state=agent_state) + code = await self.generate_execution_script(agent_state=agent_state) try: log_event( diff --git a/letta/services/tool_sandbox/local_sandbox.py b/letta/services/tool_sandbox/local_sandbox.py index 5056adde..8f2b2871 100644 --- a/letta/services/tool_sandbox/local_sandbox.py +++ b/letta/services/tool_sandbox/local_sandbox.py @@ -99,8 +99,8 @@ class AsyncToolSandboxLocal(AsyncToolSandboxBase): # Make sure sandbox directory exists sandbox_dir = os.path.expanduser(local_configs.sandbox_dir) - if not os.path.exists(sandbox_dir) or not os.path.isdir(sandbox_dir): - os.makedirs(sandbox_dir) + if not await asyncio.to_thread(lambda: os.path.exists(sandbox_dir) and os.path.isdir(sandbox_dir)): + await asyncio.to_thread(os.makedirs, sandbox_dir) # If using a virtual environment, ensure it's prepared in parallel venv_preparation_task = None @@ -109,11 +109,18 @@ class AsyncToolSandboxLocal(AsyncToolSandboxBase): venv_preparation_task = asyncio.create_task(self._prepare_venv(local_configs, venv_path, env)) # Generate and write execution script (always with markers, since we rely on stdout) - with tempfile.NamedTemporaryFile(mode="w", dir=sandbox_dir, suffix=".py", delete=False) as temp_file: - code = self.generate_execution_script(agent_state=agent_state, wrap_print_with_markers=True) - temp_file.write(code) - temp_file.flush() - temp_file_path = temp_file.name + code = await self.generate_execution_script(agent_state=agent_state, wrap_print_with_markers=True) + + async def write_temp_file(dir, content): + def _write(): + with tempfile.NamedTemporaryFile(mode="w", dir=dir, suffix=".py", delete=False) as temp_file: + temp_file.write(content) + temp_file.flush() + return temp_file.name + + return await asyncio.to_thread(_write) + + temp_file_path = await write_temp_file(sandbox_dir, code) try: # If we started a venv preparation task, wait for it to complete @@ -159,14 +166,14 @@ class AsyncToolSandboxLocal(AsyncToolSandboxBase): from letta.settings import settings if not settings.debug: - os.remove(temp_file_path) + await asyncio.to_thread(os.remove, temp_file_path) @trace_method async def _prepare_venv(self, local_configs, venv_path: str, env: Dict[str, str]): """ Prepare virtual environment asynchronously (in a background thread). """ - if self.force_recreate_venv or not os.path.isdir(venv_path): + if self.force_recreate_venv or not await asyncio.to_thread(os.path.isdir, venv_path): sandbox_dir = os.path.expanduser(local_configs.sandbox_dir) log_event(name="start create_venv_for_local_sandbox", attributes={"venv_path": venv_path}) await asyncio.to_thread( diff --git a/letta/templates/template_helper.py b/letta/templates/template_helper.py index 0d2359ce..428e2bd2 100644 --- a/letta/templates/template_helper.py +++ b/letta/templates/template_helper.py @@ -1,8 +1,10 @@ import os -from jinja2 import Environment, FileSystemLoader, StrictUndefined +from jinja2 import Environment, FileSystemLoader, StrictUndefined, Template TEMPLATE_DIR = os.path.dirname(__file__) + +# Synchronous environment (for backward compatibility) jinja_env = Environment( loader=FileSystemLoader(TEMPLATE_DIR), undefined=StrictUndefined, @@ -10,7 +12,29 @@ jinja_env = Environment( lstrip_blocks=True, ) +# Async-enabled environment +jinja_async_env = Environment( + loader=FileSystemLoader(TEMPLATE_DIR), + undefined=StrictUndefined, + trim_blocks=True, + lstrip_blocks=True, + enable_async=True, # Enable async support +) + def render_template(template_name: str, **kwargs): + """Synchronous template rendering function (kept for backward compatibility)""" template = jinja_env.get_template(template_name) return template.render(**kwargs) + + +async def render_template_async(template_name: str, **kwargs): + """Asynchronous template rendering function that doesn't block the event loop""" + template = jinja_async_env.get_template(template_name) + return await template.render_async(**kwargs) + + +async def render_string_async(template_string: str, **kwargs): + """Asynchronously render a template from a string""" + template = Template(template_string, enable_async=True) + return await template.render_async(**kwargs) diff --git a/tests/integration_test_async_tool_sandbox.py b/tests/integration_test_async_tool_sandbox.py index d1f599aa..bd01460e 100644 --- a/tests/integration_test_async_tool_sandbox.py +++ b/tests/integration_test_async_tool_sandbox.py @@ -908,11 +908,12 @@ def test_async_function_detection(add_integers_tool, async_add_integers_tool, te assert async_sandbox.is_async_function -def test_async_template_selection(add_integers_tool, async_add_integers_tool, test_user): +@pytest.mark.asyncio +async def test_async_template_selection(add_integers_tool, async_add_integers_tool, test_user): """Test that correct templates are selected for sync vs async functions""" # Test sync function uses regular template sync_sandbox = AsyncToolSandboxE2B(add_integers_tool.name, {}, test_user, tool_object=add_integers_tool) - sync_script = sync_sandbox.generate_execution_script(agent_state=None) + sync_script = await sync_sandbox.generate_execution_script(agent_state=None) print("=== SYNC SCRIPT ===") print(sync_script) print("=== END SYNC SCRIPT ===") @@ -921,7 +922,7 @@ def test_async_template_selection(add_integers_tool, async_add_integers_tool, te # Test async function uses async template async_sandbox = AsyncToolSandboxE2B(async_add_integers_tool.name, {}, test_user, tool_object=async_add_integers_tool) - async_script = async_sandbox.generate_execution_script(agent_state=None) + async_script = await async_sandbox.generate_execution_script(agent_state=None) print("=== ASYNC SCRIPT ===") print(async_script) print("=== END ASYNC SCRIPT ===") From 87a48e1695ad6b39bd9f9d5ec7dc4a380c209d11 Mon Sep 17 00:00:00 2001 From: cthomas Date: Sat, 26 Jul 2025 23:38:50 -0700 Subject: [PATCH 24/24] fix: sources test (#3581) --- tests/test_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_client.py b/tests/test_client.py index 2f9d881d..af24a6c3 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -593,6 +593,7 @@ def test_attach_detach_agent_source(client: Letta, agent: AgentState): # Create a source source = client.sources.create( name="test_source", + embedding="letta/letta-free", ) initial_sources = client.agents.sources.list(agent_id=agent.id) assert source.id not in [s.id for s in initial_sources]