From 215cfb09b60c76cd4432ee73d6473c7c904d5b11 Mon Sep 17 00:00:00 2001 From: "alex.gatto" Date: Thu, 18 Sep 2025 12:02:01 -0400 Subject: [PATCH 1/5] Update ruff + fix some toml syntax errors --- libs/aws/pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/aws/pyproject.toml b/libs/aws/pyproject.toml index f8da856a..556643a1 100644 --- a/libs/aws/pyproject.toml +++ b/libs/aws/pyproject.toml @@ -60,7 +60,7 @@ optional = true optional = true [tool.poetry.group.lint.dependencies] -ruff = "^0.1.8" +ruff = "^0.13.0" @@ -84,8 +84,8 @@ select = [ ] [tool.mypy] -ignore_missing_imports = "True" -disallow_untyped_defs = "True" +ignore_missing_imports = true +disallow_untyped_defs = true exclude = ["notebooks", "samples"] [tool.coverage.run] From a343120921a5a7d7744df8e25908edd38810eb8d Mon Sep 17 00:00:00 2001 From: "alex.gatto" Date: Thu, 18 Sep 2025 12:02:15 -0400 Subject: [PATCH 2/5] Update the lockfile --- libs/aws/poetry.lock | 370 ++++++++++++++++++++++--------------------- 1 file changed, 186 insertions(+), 184 deletions(-) diff --git a/libs/aws/poetry.lock b/libs/aws/poetry.lock index 35b31704..ef1a7f02 100644 --- a/libs/aws/poetry.lock +++ b/libs/aws/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. [[package]] name = "annotated-types" @@ -72,15 +72,15 @@ lxml = ["lxml"] [[package]] name = "bedrock-agentcore" -version = "0.1.3" +version = "0.1.4" description = "An SDK for using Bedrock AgentCore" optional = true python-versions = ">=3.10" groups = ["main"] markers = "python_version >= \"3.10\" and extra == \"tools\"" files = [ - {file = "bedrock_agentcore-0.1.3-py3-none-any.whl", hash = "sha256:570356f11d859dafe91aa524ba8e9cac46837d7202847c615b832e318c77f632"}, - {file = "bedrock_agentcore-0.1.3.tar.gz", hash = "sha256:5a569e54844aa2d6080ca0ff1c64a42d1bd7b19ef613f760c82187041b404ec5"}, + {file = "bedrock_agentcore-0.1.4-py3-none-any.whl", hash = "sha256:d372ca4d522173be6baab5723efd3c27cc2746b73b711ead1329902a98bc9a43"}, + {file = "bedrock_agentcore-0.1.4.tar.gz", hash = "sha256:59c513df840ef66843915c7c229603732a3e7d7e76cb9d618be5663dbe1cc863"}, ] [package.dependencies] @@ -97,18 +97,18 @@ strands-agents = ["strands-agents (>=1.1.0)"] [[package]] name = "boto3" -version = "1.40.27" +version = "1.40.33" description = "The AWS SDK for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "boto3-1.40.27-py3-none-any.whl", hash = "sha256:397d8cde7924f03b25eb553d5ed69293697dbfa1ca29b07369b3fa2df8318eca"}, - {file = "boto3-1.40.27.tar.gz", hash = "sha256:bf1e0f5aa79dbeedff14926dc2eb1b57bc119fa9015a190a24b6cd5bf9a60e9a"}, + {file = "boto3-1.40.33-py3-none-any.whl", hash = "sha256:dc8a37b25f43d458d830a5988283f0a5ac38a47dd2c46cccc5bc40e47fda97c9"}, + {file = "boto3-1.40.33.tar.gz", hash = "sha256:b9548f4cfb44dc9fb7f09741c7dce34350d4a9f904ff0f7f97d699d0586f6b9a"}, ] [package.dependencies] -botocore = ">=1.40.27,<1.41.0" +botocore = ">=1.40.33,<1.41.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.14.0,<0.15.0" @@ -117,14 +117,14 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.40.27" +version = "1.40.33" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "botocore-1.40.27-py3-none-any.whl", hash = "sha256:47441c94913740f5c24abf3d7f6fa534f12705e4c6669cd2e8443f3b3ca9d7ca"}, - {file = "botocore-1.40.27.tar.gz", hash = "sha256:f7cb28668751d85adc7f17929efa684640d8e2739800a86a05d4bc38f8443d1c"}, + {file = "botocore-1.40.33-py3-none-any.whl", hash = "sha256:b9a33758913410ca0d81f30dfd9f00c4ff22c72c38fdf679864011afc73d99fd"}, + {file = "botocore-1.40.33.tar.gz", hash = "sha256:307e0cb55f4b7307bfd2090fd1c9ca14e1febece5b928144ccc0f78adfd2d864"}, ] [package.dependencies] @@ -836,14 +836,14 @@ langchain-core = ">=0.3.75,<2.0.0" [[package]] name = "langsmith" -version = "0.4.27" +version = "0.4.28" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = ">=3.9" groups = ["main", "test"] files = [ - {file = "langsmith-0.4.27-py3-none-any.whl", hash = "sha256:23708e6478d1c74ac0e428bbc92df6704993e34305fb62a0c64d2fefc35bd67f"}, - {file = "langsmith-0.4.27.tar.gz", hash = "sha256:6e8bbc425797202952d4e849431e6276e7985b44536ec0582eb96eaf9129c393"}, + {file = "langsmith-0.4.28-py3-none-any.whl", hash = "sha256:0440968566d56d38d889afa202e1ff56a238e1493aea87ceb5c3c28d41d01144"}, + {file = "langsmith-0.4.28.tar.gz", hash = "sha256:8734e6d3e16ce0085b5f7235633b0e14bc8e0c160b1c1d8ce2588f83a936e171"}, ] [package.dependencies] @@ -1052,50 +1052,50 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} [[package]] name = "mypy" -version = "1.17.1" +version = "1.18.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["typing"] files = [ - {file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"}, - {file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"}, - {file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"}, - {file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"}, - {file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"}, - {file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"}, - {file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"}, - {file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"}, - {file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"}, - {file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"}, - {file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"}, - {file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"}, - {file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"}, - {file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"}, - {file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"}, - {file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"}, - {file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"}, - {file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"}, - {file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"}, - {file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"}, - {file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"}, - {file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"}, - {file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"}, - {file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"}, - {file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"}, - {file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"}, - {file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"}, - {file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"}, - {file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"}, - {file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"}, - {file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"}, - {file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"}, - {file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"}, - {file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"}, - {file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"}, - {file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"}, - {file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"}, - {file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"}, + {file = "mypy-1.18.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2761b6ae22a2b7d8e8607fb9b81ae90bc2e95ec033fd18fa35e807af6c657763"}, + {file = "mypy-1.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b10e3ea7f2eec23b4929a3fabf84505da21034a4f4b9613cda81217e92b74f3"}, + {file = "mypy-1.18.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:261fbfced030228bc0f724d5d92f9ae69f46373bdfd0e04a533852677a11dbea"}, + {file = "mypy-1.18.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4dc6b34a1c6875e6286e27d836a35c0d04e8316beac4482d42cfea7ed2527df8"}, + {file = "mypy-1.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1cabb353194d2942522546501c0ff75c4043bf3b63069cb43274491b44b773c9"}, + {file = "mypy-1.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:738b171690c8e47c93569635ee8ec633d2cdb06062f510b853b5f233020569a9"}, + {file = "mypy-1.18.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c903857b3e28fc5489e54042684a9509039ea0aedb2a619469438b544ae1961"}, + {file = "mypy-1.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a0c8392c19934c2b6c65566d3a6abdc6b51d5da7f5d04e43f0eb627d6eeee65"}, + {file = "mypy-1.18.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f85eb7efa2ec73ef63fc23b8af89c2fe5bf2a4ad985ed2d3ff28c1bb3c317c92"}, + {file = "mypy-1.18.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82ace21edf7ba8af31c3308a61dc72df30500f4dbb26f99ac36b4b80809d7e94"}, + {file = "mypy-1.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a2dfd53dfe632f1ef5d161150a4b1f2d0786746ae02950eb3ac108964ee2975a"}, + {file = "mypy-1.18.1-cp311-cp311-win_amd64.whl", hash = "sha256:320f0ad4205eefcb0e1a72428dde0ad10be73da9f92e793c36228e8ebf7298c0"}, + {file = "mypy-1.18.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:502cde8896be8e638588b90fdcb4c5d5b8c1b004dfc63fd5604a973547367bb9"}, + {file = "mypy-1.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7509549b5e41be279afc1228242d0e397f1af2919a8f2877ad542b199dc4083e"}, + {file = "mypy-1.18.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5956ecaabb3a245e3f34100172abca1507be687377fe20e24d6a7557e07080e2"}, + {file = "mypy-1.18.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8750ceb014a96c9890421c83f0db53b0f3b8633e2864c6f9bc0a8e93951ed18d"}, + {file = "mypy-1.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fb89ea08ff41adf59476b235293679a6eb53a7b9400f6256272fb6029bec3ce5"}, + {file = "mypy-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:2657654d82fcd2a87e02a33e0d23001789a554059bbf34702d623dafe353eabf"}, + {file = "mypy-1.18.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d70d2b5baf9b9a20bc9c730015615ae3243ef47fb4a58ad7b31c3e0a59b5ef1f"}, + {file = "mypy-1.18.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b8367e33506300f07a43012fc546402f283c3f8bcff1dc338636affb710154ce"}, + {file = "mypy-1.18.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:913f668ec50c3337b89df22f973c1c8f0b29ee9e290a8b7fe01cc1ef7446d42e"}, + {file = "mypy-1.18.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a0e70b87eb27b33209fa4792b051c6947976f6ab829daa83819df5f58330c71"}, + {file = "mypy-1.18.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c378d946e8a60be6b6ede48c878d145546fb42aad61df998c056ec151bf6c746"}, + {file = "mypy-1.18.1-cp313-cp313-win_amd64.whl", hash = "sha256:2cd2c1e0f3a7465f22731987fff6fc427e3dcbb4ca5f7db5bbeaff2ff9a31f6d"}, + {file = "mypy-1.18.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ba24603c58e34dd5b096dfad792d87b304fc6470cbb1c22fd64e7ebd17edcc61"}, + {file = "mypy-1.18.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ed36662fb92ae4cb3cacc682ec6656208f323bbc23d4b08d091eecfc0863d4b5"}, + {file = "mypy-1.18.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:040ecc95e026f71a9ad7956fea2724466602b561e6a25c2e5584160d3833aaa8"}, + {file = "mypy-1.18.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:937e3ed86cb731276706e46e03512547e43c391a13f363e08d0fee49a7c38a0d"}, + {file = "mypy-1.18.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1f95cc4f01c0f1701ca3b0355792bccec13ecb2ec1c469e5b85a6ef398398b1d"}, + {file = "mypy-1.18.1-cp314-cp314-win_amd64.whl", hash = "sha256:e4f16c0019d48941220ac60b893615be2f63afedaba6a0801bdcd041b96991ce"}, + {file = "mypy-1.18.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e37763af63a8018308859bc83d9063c501a5820ec5bd4a19f0a2ac0d1c25c061"}, + {file = "mypy-1.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:51531b6e94f34b8bd8b01dee52bbcee80daeac45e69ec5c36e25bce51cbc46e6"}, + {file = "mypy-1.18.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbfdea20e90e9c5476cea80cfd264d8e197c6ef2c58483931db2eefb2f7adc14"}, + {file = "mypy-1.18.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99f272c9b59f5826fffa439575716276d19cbf9654abc84a2ba2d77090a0ba14"}, + {file = "mypy-1.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8c05a7f8c00300a52f3a4fcc95a185e99bf944d7e851ff141bae8dcf6dcfeac4"}, + {file = "mypy-1.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:2fbcecbe5cf213ba294aa8c0b8c104400bf7bb64db82fb34fe32a205da4b3531"}, + {file = "mypy-1.18.1-py3-none-any.whl", hash = "sha256:b76a4de66a0ac01da1be14ecc8ae88ddea33b8380284a9e3eae39d57ebcbe26e"}, + {file = "mypy-1.18.1.tar.gz", hash = "sha256:9e988c64ad3ac5987f43f5154f884747faf62141b7f842e87465b45299eea5a9"}, ] [package.dependencies] @@ -1549,14 +1549,14 @@ files = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main", "test"] files = [ - {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, - {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, + {file = "pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2"}, + {file = "pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2"}, ] [package.dependencies] @@ -2024,29 +2024,31 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.1.15" +version = "0.13.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["lint"] files = [ - {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"}, - {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"}, - {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"}, - {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"}, - {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"}, - {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"}, + {file = "ruff-0.13.0-py3-none-linux_armv6l.whl", hash = "sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004"}, + {file = "ruff-0.13.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9"}, + {file = "ruff-0.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991"}, + {file = "ruff-0.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf"}, + {file = "ruff-0.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b"}, + {file = "ruff-0.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41"}, + {file = "ruff-0.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945"}, + {file = "ruff-0.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823"}, + {file = "ruff-0.13.0-py3-none-win32.whl", hash = "sha256:4e473e8f0e6a04e4113f2e1de12a5039579892329ecc49958424e5568ef4f768"}, + {file = "ruff-0.13.0-py3-none-win_amd64.whl", hash = "sha256:48e5c25c7a3713eea9ce755995767f4dcd1b0b9599b638b12946e892123d1efb"}, + {file = "ruff-0.13.0-py3-none-win_arm64.whl", hash = "sha256:ab80525317b1e1d38614addec8ac954f1b3e662de9d59114ecbf771d00cf613e"}, + {file = "ruff-0.13.0.tar.gz", hash = "sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60"}, ] [[package]] @@ -2203,15 +2205,15 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "starlette" -version = "0.47.3" +version = "0.48.0" description = "The little ASGI library that shines." optional = true python-versions = ">=3.9" groups = ["main"] markers = "python_version >= \"3.10\" and extra == \"tools\"" files = [ - {file = "starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51"}, - {file = "starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9"}, + {file = "starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659"}, + {file = "starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46"}, ] [package.dependencies] @@ -2314,15 +2316,15 @@ types-urllib3 = "*" [[package]] name = "types-requests" -version = "2.32.4.20250809" +version = "2.32.4.20250913" description = "Typing stubs for requests" optional = false python-versions = ">=3.9" groups = ["typing"] -markers = "python_version >= \"3.10\" and platform_python_implementation != \"PyPy\"" +markers = "platform_python_implementation != \"PyPy\" and python_version >= \"3.10\"" files = [ - {file = "types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163"}, - {file = "types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3"}, + {file = "types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1"}, + {file = "types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d"}, ] [package.dependencies] @@ -2393,7 +2395,7 @@ description = "HTTP library with thread-safe connection pooling, file post, and optional = false python-versions = ">=3.9" groups = ["main", "test", "typing"] -markers = "python_version >= \"3.10\" and platform_python_implementation != \"PyPy\"" +markers = "platform_python_implementation != \"PyPy\" and python_version >= \"3.10\"" files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, @@ -2730,115 +2732,115 @@ type = ["pytest-mypy"] [[package]] name = "zstandard" -version = "0.24.0" +version = "0.25.0" description = "Zstandard bindings for Python" optional = false python-versions = ">=3.9" groups = ["main", "test"] files = [ - {file = "zstandard-0.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:af1394c2c5febc44e0bbf0fc6428263fa928b50d1b1982ce1d870dc793a8e5f4"}, - {file = "zstandard-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e941654cef13a1d53634ec30933722eda11f44f99e1d0bc62bbce3387580d50"}, - {file = "zstandard-0.24.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:561123d05681197c0e24eb8ab3cfdaf299e2b59c293d19dad96e1610ccd8fbc6"}, - {file = "zstandard-0.24.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0f6d9a146e07458cb41423ca2d783aefe3a3a97fe72838973c13b8f1ecc7343a"}, - {file = "zstandard-0.24.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf02f915fa7934ea5dfc8d96757729c99a8868b7c340b97704795d6413cf5fe6"}, - {file = "zstandard-0.24.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:35f13501a8accf834457d8e40e744568287a215818778bc4d79337af2f3f0d97"}, - {file = "zstandard-0.24.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92be52ca4e6e604f03d5daa079caec9e04ab4cbf6972b995aaebb877d3d24e13"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c9c3cba57f5792532a3df3f895980d47d78eda94b0e5b800651b53e96e0b604"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dd91b0134a32dfcd8be504e8e46de44ad0045a569efc25101f2a12ccd41b5759"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d6975f2d903bc354916a17b91a7aaac7299603f9ecdb788145060dde6e573a16"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7ac6e4d727521d86d20ec291a3f4e64a478e8a73eaee80af8f38ec403e77a409"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:87ae1684bc3c02d5c35884b3726525eda85307073dbefe68c3c779e104a59036"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:7de5869e616d426b56809be7dc6dba4d37b95b90411ccd3de47f421a42d4d42c"}, - {file = "zstandard-0.24.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:388aad2d693707f4a0f6cc687eb457b33303d6b57ecf212c8ff4468c34426892"}, - {file = "zstandard-0.24.0-cp310-cp310-win32.whl", hash = "sha256:962ea3aecedcc944f8034812e23d7200d52c6e32765b8da396eeb8b8ffca71ce"}, - {file = "zstandard-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:869bf13f66b124b13be37dd6e08e4b728948ff9735308694e0b0479119e08ea7"}, - {file = "zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e"}, - {file = "zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68"}, - {file = "zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb"}, - {file = "zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42"}, - {file = "zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13"}, - {file = "zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382"}, - {file = "zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848"}, - {file = "zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3"}, - {file = "zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61"}, - {file = "zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd"}, - {file = "zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34"}, - {file = "zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3"}, - {file = "zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5"}, - {file = "zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8"}, - {file = "zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f"}, - {file = "zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00"}, - {file = "zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a"}, - {file = "zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da"}, - {file = "zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777"}, - {file = "zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32"}, - {file = "zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895"}, - {file = "zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606"}, - {file = "zstandard-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e4ebb000c0fe24a6d0f3534b6256844d9dbf042fdf003efe5cf40690cf4e0f3e"}, - {file = "zstandard-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:498f88f5109666c19531f0243a90d2fdd2252839cd6c8cc6e9213a3446670fa8"}, - {file = "zstandard-0.24.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0a9e95ceb180ccd12a8b3437bac7e8a8a089c9094e39522900a8917745542184"}, - {file = "zstandard-0.24.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bcf69e0bcddbf2adcfafc1a7e864edcc204dd8171756d3a8f3340f6f6cc87b7b"}, - {file = "zstandard-0.24.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:10e284748a7e7fbe2815ca62a9d6e84497d34cfdd0143fa9e8e208efa808d7c4"}, - {file = "zstandard-0.24.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1bda8a85e5b9d5e73af2e61b23609a8cc1598c1b3b2473969912979205a1ff25"}, - {file = "zstandard-0.24.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b14bc92af065d0534856bf1b30fc48753163ea673da98857ea4932be62079b1"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:b4f20417a4f511c656762b001ec827500cbee54d1810253c6ca2df2c0a307a5f"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:337572a7340e1d92fd7fb5248c8300d0e91071002d92e0b8cabe8d9ae7b58159"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df4be1cf6e8f0f2bbe2a3eabfff163ef592c84a40e1a20a8d7db7f27cfe08fc2"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6885ae4b33aee8835dbdb4249d3dfec09af55e705d74d9b660bfb9da51baaa8b"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:663848a8bac4fdbba27feea2926049fdf7b55ec545d5b9aea096ef21e7f0b079"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:05d27c953f2e0a3ecc8edbe91d6827736acc4c04d0479672e0400ccdb23d818c"}, - {file = "zstandard-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77b8b7b98893eaf47da03d262816f01f251c2aa059c063ed8a45c50eada123a5"}, - {file = "zstandard-0.24.0-cp313-cp313-win32.whl", hash = "sha256:cf7fbb4e54136e9a03c7ed7691843c4df6d2ecc854a2541f840665f4f2bb2edd"}, - {file = "zstandard-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:d64899cc0f33a8f446f1e60bffc21fa88b99f0e8208750d9144ea717610a80ce"}, - {file = "zstandard-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:57be3abb4313e0dd625596376bbb607f40059d801d51c1a1da94d7477e63b255"}, - {file = "zstandard-0.24.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b7fa260dd2731afd0dfa47881c30239f422d00faee4b8b341d3e597cface1483"}, - {file = "zstandard-0.24.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e05d66239d14a04b4717998b736a25494372b1b2409339b04bf42aa4663bf251"}, - {file = "zstandard-0.24.0-cp314-cp314-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:622e1e04bd8a085994e02313ba06fbcf4f9ed9a488c6a77a8dbc0692abab6a38"}, - {file = "zstandard-0.24.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:55872e818598319f065e8192ebefecd6ac05f62a43f055ed71884b0a26218f41"}, - {file = "zstandard-0.24.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bb2446a55b3a0fd8aa02aa7194bd64740015464a2daaf160d2025204e1d7c282"}, - {file = "zstandard-0.24.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2825a3951f945fb2613ded0f517d402b1e5a68e87e0ee65f5bd224a8333a9a46"}, - {file = "zstandard-0.24.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09887301001e7a81a3618156bc1759e48588de24bddfdd5b7a4364da9a8fbc20"}, - {file = "zstandard-0.24.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:98ca91dc9602cf351497d5600aa66e6d011a38c085a8237b370433fcb53e3409"}, - {file = "zstandard-0.24.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e69f8e534b4e254f523e2f9d4732cf9c169c327ca1ce0922682aac9a5ee01155"}, - {file = "zstandard-0.24.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:444633b487a711e34f4bccc46a0c5dfbe1aee82c1a511e58cdc16f6bd66f187c"}, - {file = "zstandard-0.24.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f7d3fe9e1483171e9183ffdb1fab07c5fef80a9c3840374a38ec2ab869ebae20"}, - {file = "zstandard-0.24.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:27b6fa72b57824a3f7901fc9cc4ce1c1c834b28f3a43d1d4254c64c8f11149d4"}, - {file = "zstandard-0.24.0-cp314-cp314-win32.whl", hash = "sha256:fdc7a52a4cdaf7293e10813fd6a3abc0c7753660db12a3b864ab1fb5a0c60c16"}, - {file = "zstandard-0.24.0-cp314-cp314-win_amd64.whl", hash = "sha256:656ed895b28c7e42dd5b40dfcea3217cfc166b6b7eef88c3da2f5fc62484035b"}, - {file = "zstandard-0.24.0-cp314-cp314-win_arm64.whl", hash = "sha256:0101f835da7de08375f380192ff75135527e46e3f79bef224e3c49cb640fef6a"}, - {file = "zstandard-0.24.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52788e7c489069e317fde641de41b757fa0ddc150e06488f153dd5daebac7192"}, - {file = "zstandard-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ec194197e90ca063f5ecb935d6c10063d84208cac5423c07d0f1a09d1c2ea42b"}, - {file = "zstandard-0.24.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e91a4e5d62da7cb3f53e04fe254f1aa41009af578801ee6477fe56e7bef74ee2"}, - {file = "zstandard-0.24.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fc67eb15ed573950bc6436a04b3faea6c36c7db98d2db030d48391c6736a0dc"}, - {file = "zstandard-0.24.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f6ae9fc67e636fc0fa9adee39db87dfbdeabfa8420bc0e678a1ac8441e01b22b"}, - {file = "zstandard-0.24.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ab2357353894a5ec084bb8508ff892aa43fb7fe8a69ad310eac58221ee7f72aa"}, - {file = "zstandard-0.24.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f578fab202f4df67a955145c3e3ca60ccaaaf66c97808545b2625efeecdef10"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c39d2b6161f3c5c5d12e9207ecf1006bb661a647a97a6573656b09aaea3f00ef"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dc5654586613aebe5405c1ba180e67b3f29e7d98cf3187c79efdcc172f39457"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b91380aefa9c7ac831b011368daf378d3277e0bdeb6bad9535e21251e26dd55a"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:010302face38c9a909b8934e3bf6038266d6afc69523f3efa023c5cb5d38271b"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:3aa3b4344b206941385a425ea25e6dd63e5cb0f535a4b88d56e3f8902086be9e"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:63d39b161000aeeaa06a1cb77c9806e939bfe460dfd593e4cbf24e6bc717ae94"}, - {file = "zstandard-0.24.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ed8345b504df1cab280af923ef69ec0d7d52f7b22f78ec7982fde7c33a43c4f"}, - {file = "zstandard-0.24.0-cp39-cp39-win32.whl", hash = "sha256:1e133a9dd51ac0bcd5fd547ba7da45a58346dbc63def883f999857b0d0c003c4"}, - {file = "zstandard-0.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:8ecd3b1f7a601f79e0cd20c26057d770219c0dc2f572ea07390248da2def79a4"}, - {file = "zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f"}, + {file = "zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd"}, + {file = "zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0"}, + {file = "zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e"}, + {file = "zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74"}, + {file = "zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa"}, + {file = "zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e"}, + {file = "zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c"}, + {file = "zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6"}, + {file = "zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa"}, + {file = "zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7"}, + {file = "zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4"}, + {file = "zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2"}, + {file = "zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137"}, + {file = "zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b"}, + {file = "zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a"}, + {file = "zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512"}, + {file = "zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa"}, + {file = "zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd"}, + {file = "zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01"}, + {file = "zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9"}, + {file = "zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94"}, + {file = "zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551"}, + {file = "zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98"}, + {file = "zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf"}, + {file = "zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09"}, + {file = "zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5"}, + {file = "zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049"}, + {file = "zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3"}, + {file = "zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859"}, + {file = "zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c"}, + {file = "zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088"}, + {file = "zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12"}, + {file = "zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2"}, + {file = "zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d"}, + {file = "zstandard-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b9af1fe743828123e12b41dd8091eca1074d0c1569cc42e6e1eee98027f2bbd0"}, + {file = "zstandard-0.25.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b14abacf83dfb5c25eb4e4a79520de9e7e205f72c9ee7702f91233ae57d33a2"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:a51ff14f8017338e2f2e5dab738ce1ec3b5a851f23b18c1ae1359b1eecbee6df"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3b870ce5a02d4b22286cf4944c628e0f0881b11b3f14667c1d62185a99e04f53"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:05353cef599a7b0b98baca9b068dd36810c3ef0f42bf282583f438caf6ddcee3"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19796b39075201d51d5f5f790bf849221e58b48a39a5fc74837675d8bafc7362"}, + {file = "zstandard-0.25.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53e08b2445a6bc241261fea89d065536f00a581f02535f8122eba42db9375530"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1f3689581a72eaba9131b1d9bdbfe520ccd169999219b41000ede2fca5c1bfdb"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d8c56bb4e6c795fc77d74d8e8b80846e1fb8292fc0b5060cd8131d522974b751"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:53f94448fe5b10ee75d246497168e5825135d54325458c4bfffbaafabcc0a577"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c2ba942c94e0691467ab901fc51b6f2085ff48f2eea77b1a48240f011e8247c7"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:07b527a69c1e1c8b5ab1ab14e2afe0675614a09182213f21a0717b62027b5936"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:51526324f1b23229001eb3735bc8c94f9c578b1bd9e867a0a646a3b17109f388"}, + {file = "zstandard-0.25.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89c4b48479a43f820b749df49cd7ba2dbc2b1b78560ecb5ab52985574fd40b27"}, + {file = "zstandard-0.25.0-cp39-cp39-win32.whl", hash = "sha256:1cd5da4d8e8ee0e88be976c294db744773459d51bb32f707a0f166e5ad5c8649"}, + {file = "zstandard-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:37daddd452c0ffb65da00620afb8e17abd4adaae6ce6310702841760c2c26860"}, + {file = "zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b"}, ] [package.extras] -cffi = ["cffi (>=1.17) ; python_version >= \"3.13\" and platform_python_implementation != \"PyPy\""] +cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and python_version < \"3.14\"", "cffi (>=2.0.0b) ; platform_python_implementation != \"PyPy\" and python_version >= \"3.14\""] [extras] tools = ["beautifulsoup4", "bedrock-agentcore", "playwright"] @@ -2846,4 +2848,4 @@ tools = ["beautifulsoup4", "bedrock-agentcore", "playwright"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "cbba20a11aa1fe3e207f8ba357056f332abf4f9f1a7cf7b4e2c8ebaf6a282930" +content-hash = "7c3a1a9cff1cc9a9e77e1c4726b32415ae410ccf7107e60cb6ac10ab88d57797" From 6526ad0ef160764b07c2bb3691d867406db6e000 Mon Sep 17 00:00:00 2001 From: "alex.gatto" Date: Thu, 18 Sep 2025 12:14:25 -0400 Subject: [PATCH 3/5] ran the formatter and this is what happened --- libs/aws/langchain_aws/chat_models/bedrock.py | 125 ++++---- .../chat_models/bedrock_converse.py | 6 +- .../chat_models/sagemaker_endpoint.py | 27 +- .../document_compressors/rerank.py | 13 +- libs/aws/langchain_aws/embeddings/bedrock.py | 4 +- .../aws/langchain_aws/graphs/neptune_graph.py | 18 +- .../langchain_aws/graphs/neptune_rdf_graph.py | 2 +- libs/aws/langchain_aws/llms/bedrock.py | 77 +++-- .../langchain_aws/llms/sagemaker_endpoint.py | 2 +- libs/aws/langchain_aws/retrievers/bedrock.py | 131 ++++----- libs/aws/langchain_aws/runnables/__init__.py | 2 +- .../aws/langchain_aws/runnables/q_business.py | 50 ++-- libs/aws/langchain_aws/tools/__init__.py | 5 +- .../tools/browser_session_manager.py | 22 +- .../langchain_aws/tools/browser_toolkit.py | 2 +- libs/aws/langchain_aws/tools/browser_tools.py | 119 ++++---- .../tools/code_interpreter_toolkit.py | 33 +-- libs/aws/langchain_aws/tools/utils.py | 4 +- libs/aws/langchain_aws/utils.py | 29 +- .../chat_models/test_bedrock.py | 81 +++--- .../chat_models/test_sagemaker_endpoint.py | 4 +- .../embeddings/test_bedrock_embeddings.py | 5 +- .../integration_tests/llms/test_bedrock.py | 4 +- .../unit_tests/chat_models/test_bedrock.py | 262 +++++++++-------- .../chat_models/test_bedrock_converse.py | 9 +- .../chat_models/test_sagemaker_endpoint.py | 1 + .../aws/tests/unit_tests/llms/test_bedrock.py | 77 ++--- .../unit_tests/retrievers/test_bedrock.py | 17 +- libs/aws/tests/unit_tests/test_utils.py | 267 ++++++++++-------- 29 files changed, 760 insertions(+), 638 deletions(-) diff --git a/libs/aws/langchain_aws/chat_models/bedrock.py b/libs/aws/langchain_aws/chat_models/bedrock.py index ee8554d9..e809472c 100644 --- a/libs/aws/langchain_aws/chat_models/bedrock.py +++ b/libs/aws/langchain_aws/chat_models/bedrock.py @@ -98,16 +98,15 @@ def _convert_one_message_to_text_llama3(message: BaseMessage) -> str: ) elif isinstance(message, HumanMessage): message_text = ( - f"<|start_header_id|>user" f"<|end_header_id|>{message.content}<|eot_id|>" + f"<|start_header_id|>user<|end_header_id|>{message.content}<|eot_id|>" ) elif isinstance(message, AIMessage): message_text = ( - f"<|start_header_id|>assistant" - f"<|end_header_id|>{message.content}<|eot_id|>" + f"<|start_header_id|>assistant<|end_header_id|>{message.content}<|eot_id|>" ) elif isinstance(message, SystemMessage): message_text = ( - f"<|start_header_id|>system" f"<|end_header_id|>{message.content}<|eot_id|>" + f"<|start_header_id|>system<|end_header_id|>{message.content}<|eot_id|>" ) else: raise ValueError(f"Got unknown type {message}") @@ -131,21 +130,15 @@ def _convert_one_message_to_text_llama4(message: BaseMessage) -> str: f"<|header_start|>{message.role}<|header_end|>{message.content}<|eot|>" ) elif isinstance(message, HumanMessage): - message_text = ( - f"<|header_start|>user<|header_end|>{message.content}<|eot|>" - ) + message_text = f"<|header_start|>user<|header_end|>{message.content}<|eot|>" elif isinstance(message, AIMessage): message_text = ( f"<|header_start|>assistant<|header_end|>{message.content}<|eot|>" ) elif isinstance(message, SystemMessage): - message_text = ( - f"<|header_start|>system<|header_end|>{message.content}<|eot|>" - ) + message_text = f"<|header_start|>system<|header_end|>{message.content}<|eot|>" elif isinstance(message, ToolMessage): - message_text = ( - f"<|header_start|>ipython<|header_end|>{message.content}<|eom|>" - ) + message_text = f"<|header_start|>ipython<|header_end|>{message.content}<|eom|>" else: raise ValueError(f"Got unknown type {message}") @@ -197,7 +190,7 @@ def convert_messages_to_prompt_anthropic( """ if messages is None: return "" - + messages = messages.copy() # don't mutate the original list if len(messages) > 0 and not isinstance(messages[-1], AIMessage): messages.append(AIMessage(content="")) @@ -234,21 +227,13 @@ def convert_messages_to_prompt_mistral(messages: List[BaseMessage]) -> str: def _convert_one_message_to_text_deepseek(message: BaseMessage) -> str: if isinstance(message, ChatMessage): - message_text = ( - f"<|{message.role}|>{message.content}" - ) + message_text = f"<|{message.role}|>{message.content}" elif isinstance(message, HumanMessage): - message_text = ( - f"<|User|>{message.content}" - ) + message_text = f"<|User|>{message.content}" elif isinstance(message, AIMessage): - message_text = ( - f"<|Assistant|>{message.content}" - ) + message_text = f"<|Assistant|>{message.content}" elif isinstance(message, SystemMessage): - message_text = ( - f"<|System|>{message.content}" - ) + message_text = f"<|System|>{message.content}" else: raise ValueError(f"Got unknown type {message}") @@ -291,30 +276,22 @@ def convert_messages_to_prompt_writer(messages: List[BaseMessage]) -> str: def _convert_one_message_to_text_openai(message: BaseMessage) -> str: if isinstance(message, SystemMessage): - message_text = ( - f"<|start|>system<|message|>{message.content}<|end|>" - ) + message_text = f"<|start|>system<|message|>{message.content}<|end|>" elif isinstance(message, ChatMessage): # developer role messages - message_text = ( - f"<|start|>{message.role}<|message|>{message.content}<|end|>" - ) + message_text = f"<|start|>{message.role}<|message|>{message.content}<|end|>" elif isinstance(message, HumanMessage): - message_text = ( - f"<|start|>user<|message|>{message.content}<|end|>" - ) + message_text = f"<|start|>user<|message|>{message.content}<|end|>" elif isinstance(message, AIMessage): message_text = ( f"<|start|>assistant<|channel|>final<|message|>{message.content}<|end|>" ) elif isinstance(message, ToolMessage): - # TODO: Tool messages in the OpenAI format should use "<|start|>{toolname} to=assistant<|message|>" - # Need to extract the tool name from the ToolMessage content or tool_call_id - # For now using generic "to=assistant" format as placeholder until we implement tool calling - # Will be resolved in follow-up PR with full tool support - message_text = ( - f"<|start|>to=assistant<|channel|>commentary<|message|>{message.content}<|end|>" - ) + # TODO: Tool messages in the OpenAI format should use "<|start|>{toolname} to=assistant<|message|>" + # Need to extract the tool name from the ToolMessage content or tool_call_id + # For now using generic "to=assistant" format as placeholder until we implement tool calling + # Will be resolved in follow-up PR with full tool support + message_text = f"<|start|>to=assistant<|channel|>commentary<|message|>{message.content}<|end|>" else: raise ValueError(f"Got unknown type {message}") @@ -373,7 +350,7 @@ def _format_data_content_block(block: dict) -> dict: "type": "base64", "media_type": block["mime_type"], "data": block["data"], - } + }, } else: error_message = "Image data only supported through in-line base64 format." @@ -440,9 +417,7 @@ def _format_anthropic_messages( for i, message in enumerate(merged_messages): if message.type == "system": if system is not None: - raise ValueError( - "Received multiple non-consecutive system messages." - ) + raise ValueError("Received multiple non-consecutive system messages.") elif isinstance(message.content, str): system = message.content elif isinstance(message.content, list): @@ -482,9 +457,9 @@ def _format_anthropic_messages( if not isinstance(message.content, str): # parse as dict - assert isinstance( - message.content, list - ), "Anthropic message content must be str or list of dicts" + assert isinstance(message.content, list), ( + "Anthropic message content must be str or list of dicts" + ) # populate content content = [] @@ -514,19 +489,28 @@ def _format_anthropic_messages( # Handle list content inside tool_result processed_list = [] for list_item in content_item: - if isinstance(list_item, dict) and list_item.get("type") == "image_url": + if ( + isinstance(list_item, dict) + and list_item.get("type") == "image_url" + ): # Process image in list - source = _format_image(list_item["image_url"]["url"]) - processed_list.append({"type": "image", "source": source}) + source = _format_image( + list_item["image_url"]["url"] + ) + processed_list.append( + {"type": "image", "source": source} + ) else: # Keep other items as is processed_list.append(list_item) # Add processed list to tool_result - tool_blocks.append({ - "type": "tool_result", - "tool_use_id": item.get("tool_use_id"), - "content": processed_list - }) + tool_blocks.append( + { + "type": "tool_result", + "tool_use_id": item.get("tool_use_id"), + "content": processed_list, + } + ) else: # For other content types, keep as is tool_blocks.append(item) @@ -788,9 +772,9 @@ def set_beta_use_converse_api(cls, values: Dict) -> Any: response = bedrock_client.get_inference_profile( inferenceProfileIdentifier=model_id ) - if 'models' in response and len(response['models']) > 0: - model_arn = response['models'][0]['modelArn'] - resolved_base_model = model_arn.split('/')[-1] + if "models" in response and len(response["models"]) > 0: + model_arn = response["models"][0]["modelArn"] + resolved_base_model = model_arn.split("/")[-1] values["beta_use_converse_api"] = "nova" in resolved_base_model return values @@ -877,7 +861,7 @@ def _stream( added_model_name = False # Track guardrails trace information for callback handling guardrails_trace_info = None - + for chunk in self._prepare_input_and_invoke_stream( prompt=prompt, system=system, @@ -902,7 +886,7 @@ def _stream( if services_trace.get("signal") and run_manager: # Store trace info for potential callback guardrails_trace_info = services_trace - + usage_metadata = generation_info.pop("usage_metadata", None) response_metadata = generation_info if not added_model_name: @@ -924,7 +908,7 @@ def _stream( generation_chunk.text, chunk=generation_chunk ) yield generation_chunk - + # If guardrails intervened during streaming, notify the callback handler if guardrails_trace_info and run_manager: run_manager.on_llm_error( @@ -987,7 +971,9 @@ def _generate( else: system = self.system_prompt_with_tools elif provider == "openai": - formatted_messages = ChatPromptAdapter.format_messages(provider, messages) + formatted_messages = ChatPromptAdapter.format_messages( + provider, messages + ) else: prompt = ChatPromptAdapter.convert_messages_to_prompt( provider=provider, messages=messages, model=self._get_base_model() @@ -1116,13 +1102,10 @@ def bind_tools( # Disallow forced tool use when thinking is enabled on specific Claude models base_model = self._get_base_model() - if ( - any( - x in base_model - for x in ("claude-3-7-", "claude-opus-4-", "claude-sonnet-4-") - ) - and thinking_in_params(self.model_kwargs or {}) - ): + if any( + x in base_model + for x in ("claude-3-7-", "claude-opus-4-", "claude-sonnet-4-") + ) and thinking_in_params(self.model_kwargs or {}): forced = False if isinstance(tool_choice, bool): forced = bool(tool_choice) diff --git a/libs/aws/langchain_aws/chat_models/bedrock_converse.py b/libs/aws/langchain_aws/chat_models/bedrock_converse.py index b31e7cfe..04c9bfd5 100644 --- a/libs/aws/langchain_aws/chat_models/bedrock_converse.py +++ b/libs/aws/langchain_aws/chat_models/bedrock_converse.py @@ -467,9 +467,9 @@ class Joke(BaseModel): additionalModelResponseFieldPaths. """ - supports_tool_choice_values: Optional[ - Sequence[Literal["auto", "any", "tool"]] - ] = None + supports_tool_choice_values: Optional[Sequence[Literal["auto", "any", "tool"]]] = ( + None + ) """Which types of tool_choice values the model supports. Inferred if not specified. Inferred as ('auto', 'any', 'tool') if a 'claude-3' diff --git a/libs/aws/langchain_aws/chat_models/sagemaker_endpoint.py b/libs/aws/langchain_aws/chat_models/sagemaker_endpoint.py index 1c54ac20..4a7dfe08 100644 --- a/libs/aws/langchain_aws/chat_models/sagemaker_endpoint.py +++ b/libs/aws/langchain_aws/chat_models/sagemaker_endpoint.py @@ -1,4 +1,5 @@ """Sagemaker Chat Model.""" + import io import logging from typing import Any, Dict, Iterator, List, Mapping, Optional @@ -136,16 +137,16 @@ class ChatSagemakerEndpoint(BaseChatModel): EC2 instance, credentials from IMDS will be used. client: boto3 client for Sagemaker Endpoint - + endpoint_name: The name of the endpoint from the deployed Sagemaker model. - content_handler: Implementation for model specific ChatContentHandler + content_handler: Implementation for model specific ChatContentHandler Example: .. code-block:: python - from langchain_aws.chat_models.sagemaker_endpoint import + from langchain_aws.chat_models.sagemaker_endpoint import ChatSagemakerEndpoint endpoint_name = ( "my-endpoint-name" @@ -161,7 +162,7 @@ class ChatSagemakerEndpoint(BaseChatModel): region_name=region_name, credentials_profile_name=credentials_profile_name ) - + # Usage with Inference Component se = ChatSagemakerEndpoint( endpoint_name=endpoint_name, @@ -190,13 +191,13 @@ class ChatSagemakerEndpoint(BaseChatModel): Must be unique within an AWS Region.""" inference_component_name: Optional[str] = None - """Optional name of the inference component to invoke + """Optional name of the inference component to invoke if specified with endpoint name.""" region_name: Optional[str] = "" - """The aws region, e.g., `us-west-2`. + """The aws region, e.g., `us-west-2`. - Falls back to AWS_REGION or AWS_DEFAULT_REGION env variable or region specified in + Falls back to AWS_REGION or AWS_DEFAULT_REGION env variable or region specified in ~/.aws/config in case it is not provided here. """ @@ -205,14 +206,14 @@ class ChatSagemakerEndpoint(BaseChatModel): Profile should either have access keys or role information specified. If not specified, the default credential profile or, if on an EC2 instance, - credentials from IMDS will be used. + credentials from IMDS will be used. See: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html """ aws_access_key_id: Optional[SecretStr] = Field( default_factory=secret_from_env("AWS_ACCESS_KEY_ID", default=None) ) - """AWS access key id. + """AWS access key id. If provided, aws_secret_access_key must also be provided. If not specified, the default credential profile or, if on an EC2 instance, @@ -225,7 +226,7 @@ class ChatSagemakerEndpoint(BaseChatModel): aws_secret_access_key: Optional[SecretStr] = Field( default_factory=secret_from_env("AWS_SECRET_ACCESS_KEY", default=None) ) - """AWS secret_access_key. + """AWS secret_access_key. If provided, aws_access_key_id must also be provided. If not specified, the default credential profile or, if on an EC2 instance, @@ -238,9 +239,9 @@ class ChatSagemakerEndpoint(BaseChatModel): aws_session_token: Optional[SecretStr] = Field( default_factory=secret_from_env("AWS_SESSION_TOKEN", default=None) ) - """AWS session token. + """AWS session token. - If provided, aws_access_key_id and aws_secret_access_key must + If provided, aws_access_key_id and aws_secret_access_key must also be provided. Not required unless using temporary credentials. See: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html @@ -275,7 +276,7 @@ class ContentHandler(ChatContentHandler): def transform_input(self, prompt: List[Dict[str, Any]], model_kwargs: Dict) -> bytes: input_str = json.dumps({prompt: prompt, **model_kwargs}) return input_str.encode('utf-8') - + def transform_output(self, output: bytes) -> BaseMessage: response_json = json.loads(output.read().decode("utf-8")) return response_json[0]["generated_text"] diff --git a/libs/aws/langchain_aws/document_compressors/rerank.py b/libs/aws/langchain_aws/document_compressors/rerank.py index 7a7e6165..b4f12638 100644 --- a/libs/aws/langchain_aws/document_compressors/rerank.py +++ b/libs/aws/langchain_aws/document_compressors/rerank.py @@ -1,4 +1,3 @@ -from copy import deepcopy from typing import Any, Dict, List, Optional, Sequence, Union from langchain_core.callbacks.manager import Callbacks @@ -22,9 +21,9 @@ class BedrockRerank(BaseDocumentCompressor): """Number of documents to return.""" region_name: Optional[str] = None - """The aws region, e.g., `us-west-2`. + """The aws region, e.g., `us-west-2`. - Falls back to AWS_REGION or AWS_DEFAULT_REGION env variable or region specified in + Falls back to AWS_REGION or AWS_DEFAULT_REGION env variable or region specified in ~/.aws/config in case it is not provided here. """ @@ -36,7 +35,7 @@ class BedrockRerank(BaseDocumentCompressor): aws_access_key_id: Optional[SecretStr] = Field( default_factory=secret_from_env("AWS_ACCESS_KEY_ID", default=None) ) - """AWS access key id. + """AWS access key id. If provided, aws_secret_access_key must also be provided. If not specified, the default credential profile or, if on an EC2 instance, @@ -49,7 +48,7 @@ class BedrockRerank(BaseDocumentCompressor): aws_secret_access_key: Optional[SecretStr] = Field( default_factory=secret_from_env("AWS_SECRET_ACCESS_KEY", default=None) ) - """AWS secret_access_key. + """AWS secret_access_key. If provided, aws_access_key_id must also be provided. If not specified, the default credential profile or, if on an EC2 instance, @@ -62,9 +61,9 @@ class BedrockRerank(BaseDocumentCompressor): aws_session_token: Optional[SecretStr] = Field( default_factory=secret_from_env("AWS_SESSION_TOKEN", default=None) ) - """AWS session token. + """AWS session token. - If provided, aws_access_key_id and aws_secret_access_key must + If provided, aws_access_key_id and aws_secret_access_key must also be provided. Not required unless using temporary credentials. See: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html diff --git a/libs/aws/langchain_aws/embeddings/bedrock.py b/libs/aws/langchain_aws/embeddings/bedrock.py index 96eb8c4f..d8d44f8d 100644 --- a/libs/aws/langchain_aws/embeddings/bedrock.py +++ b/libs/aws/langchain_aws/embeddings/bedrock.py @@ -146,7 +146,9 @@ def validate_environment(self) -> Self: return self - def _embedding_func(self, text: str, input_type: str = "search_document") -> List[float]: + def _embedding_func( + self, text: str, input_type: str = "search_document" + ) -> List[float]: """Call out to Bedrock embedding endpoint with a single text.""" # replace newlines, which can negatively affect performance. text = text.replace(os.linesep, " ") diff --git a/libs/aws/langchain_aws/graphs/neptune_graph.py b/libs/aws/langchain_aws/graphs/neptune_graph.py index 09136a14..9765f025 100644 --- a/libs/aws/langchain_aws/graphs/neptune_graph.py +++ b/libs/aws/langchain_aws/graphs/neptune_graph.py @@ -377,10 +377,10 @@ def __init__( import boto3 any_creds = bool( - credentials_profile_name or - aws_access_key_id or - aws_secret_access_key or - aws_session_token + credentials_profile_name + or aws_access_key_id + or aws_secret_access_key + or aws_session_token ) if not any_creds: @@ -393,7 +393,9 @@ def __init__( "aws_secret_access_key": aws_secret_access_key.get_secret_value(), } if aws_session_token: - session_params["aws_session_token"] = aws_session_token.get_secret_value() + session_params["aws_session_token"] = ( + aws_session_token.get_secret_value() + ) session = boto3.Session(**session_params) else: raise ValueError( @@ -459,9 +461,9 @@ def query(self, query: str, params: dict = {}) -> Dict[str, Any]: """Query Neptune database.""" try: if params: - return self.client.execute_open_cypher_query(openCypherQuery=query, parameters = json.dumps(params))[ - "results" - ] + return self.client.execute_open_cypher_query( + openCypherQuery=query, parameters=json.dumps(params) + )["results"] else: return self.client.execute_open_cypher_query(openCypherQuery=query)[ "results" diff --git a/libs/aws/langchain_aws/graphs/neptune_rdf_graph.py b/libs/aws/langchain_aws/graphs/neptune_rdf_graph.py index 92a00939..de02b0c1 100644 --- a/libs/aws/langchain_aws/graphs/neptune_rdf_graph.py +++ b/libs/aws/langchain_aws/graphs/neptune_rdf_graph.py @@ -243,7 +243,7 @@ def _get_local_name(self, iri: str) -> Sequence[str]: return [f"{tokens[0]}#", tokens[-1]] elif "/" in iri: tokens = iri.split("/") - return [f"{'/'.join(tokens[0:len(tokens)-1])}/", tokens[-1]] + return [f"{'/'.join(tokens[0 : len(tokens) - 1])}/", tokens[-1]] else: raise ValueError(f"Unexpected IRI '{iri}', contains neither '#' nor '/'.") diff --git a/libs/aws/langchain_aws/llms/bedrock.py b/libs/aws/langchain_aws/llms/bedrock.py index dc27fc29..098ae964 100644 --- a/libs/aws/langchain_aws/llms/bedrock.py +++ b/libs/aws/langchain_aws/llms/bedrock.py @@ -171,7 +171,8 @@ def _stream_response_to_generation_chunk( generation_info = { k: v for k, v in stream_response.items() - if k not in [output_key, "prompt_token_count", "generation_token_count", "created"] + if k + not in [output_key, "prompt_token_count", "generation_token_count", "created"] } return GenerationChunk( text=( @@ -236,8 +237,7 @@ def _get_invocation_metrics_chunk(chunk: Dict[str, Any]) -> GenerationChunk: "input_token_details": { "cache_creation": cache_write_input_tokens, "cache_read": cache_read_input_tokens, - } - + }, } return GenerationChunk(text="", generation_info=generation_info) @@ -273,7 +273,7 @@ class LLMInputOutputAdapter: "deepseek": "choices", "meta": "generation", "mistral": "outputs", - "writer": "choices" + "writer": "choices", } @classmethod @@ -459,8 +459,12 @@ def prepare_output(cls, provider: str, response: Any) -> dict: headers = response.get("ResponseMetadata", {}).get("HTTPHeaders", {}) prompt_tokens = int(headers.get("x-amzn-bedrock-input-token-count", 0)) completion_tokens = int(headers.get("x-amzn-bedrock-output-token-count", 0)) - cache_read_input_tokens = int(headers.get("x-amzn-bedrock-cache-read-input-token-count", 0)) - cache_write_input_tokens = int(headers.get("x-amzn-bedrock-cache-write-input-token-count", 0)) + cache_read_input_tokens = int( + headers.get("x-amzn-bedrock-cache-read-input-token-count", 0) + ) + cache_write_input_tokens = int( + headers.get("x-amzn-bedrock-cache-write-input-token-count", 0) + ) return { "text": text, "thinking": thinking, @@ -471,7 +475,9 @@ def prepare_output(cls, provider: str, response: Any) -> dict: "completion_tokens": completion_tokens, "cache_read_input_tokens": cache_read_input_tokens, "cache_write_input_tokens": cache_write_input_tokens, - "total_tokens": prompt_tokens + cache_read_input_tokens + completion_tokens, + "total_tokens": prompt_tokens + + cache_read_input_tokens + + completion_tokens, }, "stop_reason": response_body.get("stop_reason"), } @@ -527,7 +533,10 @@ def prepare_output_stream( if provider == "deepseek": opt = chunk_obj.get(output_key, [{}])[0] - if opt.get("stop_reason") in ["stop", "length"] or opt.get("finish_reason") == "eos_token": + if ( + opt.get("stop_reason") in ["stop", "length"] + or opt.get("finish_reason") == "eos_token" + ): yield _get_invocation_metrics_chunk(chunk_obj) return @@ -603,7 +612,7 @@ class BedrockBase(BaseLanguageModel, ABC): client: Any = Field(default=None, exclude=True) #: :meta private: """The bedrock runtime client for making data plane API calls""" - + bedrock_client: Any = Field(default=None, exclude=True) #: :meta private: """The bedrock client for making control plane API calls""" @@ -796,33 +805,38 @@ def validate_environment(self) -> Self: bedrock_client_cfg = {} if self.client: try: - if hasattr(self.client, 'meta') and hasattr(self.client.meta, 'region_name'): - bedrock_client_cfg['region_name'] = self.client.meta.region_name - if hasattr(self.client, '_client_config'): - bedrock_client_cfg['config'] = self.client._client_config + if hasattr(self.client, "meta") and hasattr( + self.client.meta, "region_name" + ): + bedrock_client_cfg["region_name"] = self.client.meta.region_name + if hasattr(self.client, "_client_config"): + bedrock_client_cfg["config"] = self.client._client_config except (AttributeError, TypeError): pass - + # Prioritize directly passed parameters over those extracted from client self.bedrock_client = create_aws_client( - region_name=self.region_name or bedrock_client_cfg.get('region_name'), + region_name=self.region_name or bedrock_client_cfg.get("region_name"), credentials_profile_name=self.credentials_profile_name, aws_access_key_id=self.aws_access_key_id, aws_secret_access_key=self.aws_secret_access_key, aws_session_token=self.aws_session_token, endpoint_url=self.endpoint_url, - config=self.config or bedrock_client_cfg.get('config'), + config=self.config or bedrock_client_cfg.get("config"), service_name="bedrock", ) - if self.base_model_id is None and 'application-inference-profile' in self.model_id: + if ( + self.base_model_id is None + and "application-inference-profile" in self.model_id + ): response = self.bedrock_client.get_inference_profile( - inferenceProfileIdentifier = self.model_id + inferenceProfileIdentifier=self.model_id ) - if 'models' in response and len(response['models']) > 0: - model_arn = response['models'][0]['modelArn'] + if "models" in response and len(response["models"]) > 0: + model_arn = response["models"][0]["modelArn"] # Format: arn:aws:bedrock:region::foundation-model/provider.model-name - self.base_model_id = model_arn.split('/')[-1] + self.base_model_id = model_arn.split("/")[-1] return self @@ -849,8 +863,7 @@ def _get_provider(self) -> str: # so this requires passing in the provider by user if self.model_id.startswith("arn"): raise ValueError( - "Model provider should be supplied when passing a model ARN as " - "model_id" + "Model provider should be supplied when passing a model ARN as model_id" ) # If model_id has region prefixed to them, @@ -859,12 +872,20 @@ def _get_provider(self) -> str: parts = self.model_id.split(".", maxsplit=2) return ( parts[1] - if (len(parts) > 1 and parts[0].lower() in {"eu", "us", "us-gov", "apac", "sa", "amer", "global"}) + if ( + len(parts) > 1 + and parts[0].lower() + in {"eu", "us", "us-gov", "apac", "sa", "amer", "global"} + ) else parts[0] ) def _get_base_model(self) -> str: - return self.base_model_id if self.base_model_id else self.model_id.split(".", maxsplit=1)[-1] + return ( + self.base_model_id + if self.base_model_id + else self.model_id.split(".", maxsplit=1)[-1] + ) @property def _model_is_anthropic(self) -> bool: @@ -914,7 +935,11 @@ def _prepare_input_and_invoke( params = {**_model_kwargs, **kwargs} # Pre-process for thinking with tool use - if messages and "claude-" in self._get_base_model() and thinking_in_params(params): + if ( + messages + and "claude-" in self._get_base_model() + and thinking_in_params(params) + ): # We need to ensure thinking blocks are first in assistant messages # Process each message in the sequence for i, message in enumerate(messages): diff --git a/libs/aws/langchain_aws/llms/sagemaker_endpoint.py b/libs/aws/langchain_aws/llms/sagemaker_endpoint.py index 7c5928fa..14a18b70 100644 --- a/libs/aws/langchain_aws/llms/sagemaker_endpoint.py +++ b/libs/aws/langchain_aws/llms/sagemaker_endpoint.py @@ -409,4 +409,4 @@ def _call( if stop is not None: text = enforce_stop_tokens(text, stop) - return text \ No newline at end of file + return text diff --git a/libs/aws/langchain_aws/retrievers/bedrock.py b/libs/aws/langchain_aws/retrievers/bedrock.py index 0e4d7909..fc59b41b 100644 --- a/libs/aws/langchain_aws/retrievers/bedrock.py +++ b/libs/aws/langchain_aws/retrievers/bedrock.py @@ -55,61 +55,61 @@ class RetrievalConfig(BaseModel, extra="allow"): # type: ignore[call-arg] class AmazonKnowledgeBasesRetriever(BaseRetriever): """`Amazon Bedrock Knowledge Bases` retrieval. - See https://aws.amazon.com/bedrock/knowledge-bases for more info. - - Args: - knowledge_base_id: Knowledge Base ID. - - region_name: The aws region e.g., `us-west-2`. - Fallback to AWS_REGION/AWS_DEFAULT_REGION env variable or region specified in - ~/.aws/config. - - credentials_profile_name: The name of the profile in the ~/.aws/credentials - or ~/.aws/config files, which has either access keys or role information - specified. If not specified, the default credential profile or, if on an - EC2 instance, credentials from IMDS will be used. - - aws_access_key_id: AWS access key id. If provided, aws_secret_access_key must - also be provided. If not specified, the default credential profile or, if - on an EC2 instance, credentials from IMDS will be used. See: - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html - If not provided, will be read from 'AWS_ACCESS_KEY_ID' environment variable. - - aws_secret_access_key: AWS secret_access_key. If provided, aws_access_key_id - must also be provided. If not specified, the default credential profile or, - if on an EC2 instance, credentials from IMDS will be used. See: - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html - If not provided, will be read from 'AWS_SECRET_ACCESS_KEY' environment variable. - - aws_session_token: AWS session token. If provided, aws_access_key_id and - aws_secret_access_key must also be provided. Not required unless using temporary - credentials. See: - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html - If not provided, will be read from 'AWS_SESSION_TOKEN' environment variable. - - endpoint_url: Needed if you don't want to default to us-east-1 endpoint. - - config: An optional botocore.config.Config instance to pass to the client. - - client: boto3 client for bedrock agent runtime. - - guardrail_config: Configuration information for a guardrail that you want - to use in the request. - - retrieval_config: Optional configuration for retrieval specified as a - Python object (RetrievalConfig) or as a dictionary - - Example: - .. code-block:: python - from langchain_community.retrievers import AmazonKnowledgeBasesRetriever - retriever = AmazonKnowledgeBasesRetriever( - knowledge_base_id="", - retrieval_config={ - "vectorSearchConfiguration": { - "numberOfResults": 4 - } - }, - ) + See https://aws.amazon.com/bedrock/knowledge-bases for more info. + + Args: + knowledge_base_id: Knowledge Base ID. + + region_name: The aws region e.g., `us-west-2`. + Fallback to AWS_REGION/AWS_DEFAULT_REGION env variable or region specified in + ~/.aws/config. + + credentials_profile_name: The name of the profile in the ~/.aws/credentials + or ~/.aws/config files, which has either access keys or role information + specified. If not specified, the default credential profile or, if on an + EC2 instance, credentials from IMDS will be used. + + aws_access_key_id: AWS access key id. If provided, aws_secret_access_key must + also be provided. If not specified, the default credential profile or, if + on an EC2 instance, credentials from IMDS will be used. See: + https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html + If not provided, will be read from 'AWS_ACCESS_KEY_ID' environment variable. + + aws_secret_access_key: AWS secret_access_key. If provided, aws_access_key_id + must also be provided. If not specified, the default credential profile or, + if on an EC2 instance, credentials from IMDS will be used. See: + https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html + If not provided, will be read from 'AWS_SECRET_ACCESS_KEY' environment variable. + + aws_session_token: AWS session token. If provided, aws_access_key_id and + aws_secret_access_key must also be provided. Not required unless using temporary + credentials. See: + https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html + If not provided, will be read from 'AWS_SESSION_TOKEN' environment variable. + + endpoint_url: Needed if you don't want to default to us-east-1 endpoint. + + config: An optional botocore.config.Config instance to pass to the client. + + client: boto3 client for bedrock agent runtime. + + guardrail_config: Configuration information for a guardrail that you want + to use in the request. + + retrieval_config: Optional configuration for retrieval specified as a + Python object (RetrievalConfig) or as a dictionary + + Example: + .. code-block:: python + from langchain_community.retrievers import AmazonKnowledgeBasesRetriever + retriever = AmazonKnowledgeBasesRetriever( + knowledge_base_id="", + retrieval_config={ + "vectorSearchConfiguration": { + "numberOfResults": 4 + } + }, + ) """ knowledge_base_id: str @@ -127,9 +127,7 @@ class AmazonKnowledgeBasesRetriever(BaseRetriever): endpoint_url: Optional[str] = None config: Any = None client: Any = None - guardrail_config: Optional[Dict[str, Any]] = Field( - default=None, alias="guardrails" - ) + guardrail_config: Optional[Dict[str, Any]] = Field(default=None, alias="guardrails") retrieval_config: Optional[Union[RetrievalConfig, Dict[str, Any]]] = None min_score_confidence: Annotated[ Optional[float], Field(ge=0.0, le=1.0, default=None) @@ -148,7 +146,8 @@ def create_client(cls, values: Dict[str, Any]) -> Any: aws_secret_access_key=values.get("aws_secret_access_key"), aws_session_token=values.get("aws_session_token"), endpoint_url=values.get("endpoint_url"), - config=values.get("config") or Config( + config=values.get("config") + or Config( connect_timeout=120, read_timeout=120, retries={"max_attempts": 0} ), service_name="bedrock-agent-runtime", @@ -189,9 +188,9 @@ def _get_relevant_documents( retrieve_request: Dict[str, Any] = self._get_retrieve_request(query) response = self.client.retrieve(**retrieve_request) results = response["retrievalResults"] - documents: List[ - Document - ] = AmazonKnowledgeBasesRetriever._retrieval_results_to_documents(results) + documents: List[Document] = ( + AmazonKnowledgeBasesRetriever._retrieval_results_to_documents(results) + ) return self._filter_by_score_confidence(docs=documents) @@ -207,8 +206,10 @@ def _get_retrieve_request(self, query: str) -> Dict[str, Any]: "knowledgeBaseId": self.knowledge_base_id, } if self.guardrail_config: - if not (self.guardrail_config.get("guardrailId") - and self.guardrail_config.get("guardrailVersion")): + if not ( + self.guardrail_config.get("guardrailId") + and self.guardrail_config.get("guardrailVersion") + ): raise TypeError( "Guardrail configuration must be a dictionary with both 'guardrailId' " "and 'guardrailVersion' keys." @@ -260,7 +261,7 @@ def _get_content_from_result(result: Dict[str, Any]) -> Optional[str]: """ if not result: raise ValueError("Invalid search result") - content: dict = result.get("content") + content: dict | None = result.get("content", None) if not content: raise ValueError( "Invalid search result, content is missing from the result" diff --git a/libs/aws/langchain_aws/runnables/__init__.py b/libs/aws/langchain_aws/runnables/__init__.py index dbc787e2..2065a405 100644 --- a/libs/aws/langchain_aws/runnables/__init__.py +++ b/libs/aws/langchain_aws/runnables/__init__.py @@ -1,3 +1,3 @@ from langchain_aws.runnables.q_business import AmazonQ -__all__ = ["AmazonQ"] \ No newline at end of file +__all__ = ["AmazonQ"] diff --git a/libs/aws/langchain_aws/runnables/q_business.py b/libs/aws/langchain_aws/runnables/q_business.py index fcc51b93..19de2246 100644 --- a/libs/aws/langchain_aws/runnables/q_business.py +++ b/libs/aws/langchain_aws/runnables/q_business.py @@ -13,7 +13,9 @@ @beta(message="This API is in beta and can change in future.") -class AmazonQ(Runnable[Union[str,ChatPromptValue, List[ChatPromptValue]], ChatPromptValue]): +class AmazonQ( + Runnable[Union[str, ChatPromptValue, List[ChatPromptValue]], ChatPromptValue] +): """Amazon Q Runnable wrapper. To authenticate, the AWS client uses the following methods to @@ -67,9 +69,9 @@ def __init__( def invoke( self, - input: Union[str,ChatPromptValue], + input: Union[str, ChatPromptValue], config: Optional[RunnableConfig] = None, - **kwargs: Any + **kwargs: Any, ) -> ChatPromptValue: """Call out to Amazon Q service. @@ -89,24 +91,30 @@ def invoke( response = model.invoke("Tell me a joke") """ try: - # Prepare the request + # Prepare the request request = { - 'applicationId': self.application_id, - 'userMessage': self.convert_langchain_messages_to_q_input(input), # Langchain's input comes in the form of an array of "messages". We must convert to a single string for Amazon Q's use - 'chatMode': self.chat_mode, + "applicationId": self.application_id, + "userMessage": self.convert_langchain_messages_to_q_input( + input + ), # Langchain's input comes in the form of an array of "messages". We must convert to a single string for Amazon Q's use + "chatMode": self.chat_mode, } if self.conversation_id: - request.update({ - 'conversationId': self.conversation_id, - 'parentMessageId': self.parent_message_id, - }) + request.update( + { + "conversationId": self.conversation_id, + "parentMessageId": self.parent_message_id, + } + ) # Call Amazon Q response = self.client.chat_sync(**request) # Extract the response text - if 'systemMessage' in response: - return AIMessage(content=response["systemMessage"], response_metadata=response) + if "systemMessage" in response: + return AIMessage( + content=response["systemMessage"], response_metadata=response + ) else: raise ValueError("Unexpected response format from Amazon Q") @@ -119,7 +127,7 @@ def invoke( def validate_environment(self) -> Self: """Don't do anything if client provided externally""" - #If the client is not provided, and the user_id is not provided in the class constructor, throw an error saying one or the other needs to be provided + # If the client is not provided, and the user_id is not provided in the class constructor, throw an error saying one or the other needs to be provided if self.credentials is None: raise ValueError( "Either the credentials or the client needs to be provided." @@ -131,10 +139,12 @@ def validate_environment(self) -> Self: try: if self.region_name is not None: - client = boto3.client('qbusiness', self.region_name, **self.credentials) + client = boto3.client( + "qbusiness", self.region_name, **self.credentials + ) else: # use default region - client = boto3.client('qbusiness', **self.credentials) + client = boto3.client("qbusiness", **self.credentials) except Exception as e: raise ValueError( @@ -149,9 +159,11 @@ def validate_environment(self) -> Self: "Please install it with `pip install boto3`." ) return client - def convert_langchain_messages_to_q_input(self, input: Union[str,ChatPromptValue,List[ChatPromptValue]]) -> str: - #If it is just a string and not a ChatPromptTemplate collection just return string + + def convert_langchain_messages_to_q_input( + self, input: Union[str, ChatPromptValue, List[ChatPromptValue]] + ) -> str: + # If it is just a string and not a ChatPromptTemplate collection just return string if type(input) is str: return input return input.to_string() - \ No newline at end of file diff --git a/libs/aws/langchain_aws/tools/__init__.py b/libs/aws/langchain_aws/tools/__init__.py index 5be07c0c..49c41ee0 100644 --- a/libs/aws/langchain_aws/tools/__init__.py +++ b/libs/aws/langchain_aws/tools/__init__.py @@ -1,7 +1,4 @@ from .browser_toolkit import create_browser_toolkit from .code_interpreter_toolkit import create_code_interpreter_toolkit -__all__ = [ - "create_browser_toolkit", - "create_code_interpreter_toolkit" -] \ No newline at end of file +__all__ = ["create_browser_toolkit", "create_code_interpreter_toolkit"] diff --git a/libs/aws/langchain_aws/tools/browser_session_manager.py b/libs/aws/langchain_aws/tools/browser_session_manager.py index dc760747..6bbe2752 100644 --- a/libs/aws/langchain_aws/tools/browser_session_manager.py +++ b/libs/aws/langchain_aws/tools/browser_session_manager.py @@ -19,11 +19,11 @@ class BrowserSessionManager: This class maintains separate browser sessions for different threads, enabling concurrent usage of browsers in multi-threaded environments. Browsers are created lazily only when needed by tools. - - Concurrency protection is also implemented. Each browser session is tied - to a specific thread_id and includes protection against concurrent usage. - When a browser is obtained via get_async_browser() or get_sync_browser(), - it is marked as "in use", and subsequent attempts to access the same + + Concurrency protection is also implemented. Each browser session is tied + to a specific thread_id and includes protection against concurrent usage. + When a browser is obtained via get_async_browser() or get_sync_browser(), + it is marked as "in use", and subsequent attempts to access the same browser session will raise a RuntimeError until it is released. In general, different callers should use different thread_ids to avoid concurrency issues. """ @@ -73,7 +73,7 @@ def get_sync_browser(self, thread_id: str) -> SyncBrowser: Returns: A sync browser instance specific to the thread - + Raises: RuntimeError: If the browser session is already in use by another caller """ @@ -205,30 +205,30 @@ async def release_async_browser(self, thread_id: str) -> None: Args: thread_id: Unique identifier for the thread - + Raises: KeyError: If no browser session exists for the specified thread_id """ if thread_id not in self._async_sessions: raise KeyError(f"No async browser session found for thread {thread_id}") - + client, browser, _ = self._async_sessions[thread_id] self._async_sessions[thread_id] = (client, browser, False) logger.debug(f"Async browser session released for thread {thread_id}") - + def release_sync_browser(self, thread_id: str) -> None: """ Release the sync browser session for the specified thread. Args: thread_id: Unique identifier for the thread - + Raises: KeyError: If no browser session exists for the specified thread_id """ if thread_id not in self._sync_sessions: raise KeyError(f"No sync browser session found for thread {thread_id}") - + client, browser, _ = self._sync_sessions[thread_id] self._sync_sessions[thread_id] = (client, browser, False) logger.debug(f"Sync browser session released for thread {thread_id}") diff --git a/libs/aws/langchain_aws/tools/browser_toolkit.py b/libs/aws/langchain_aws/tools/browser_toolkit.py index 4f1f81ca..91e6a178 100644 --- a/libs/aws/langchain_aws/tools/browser_toolkit.py +++ b/libs/aws/langchain_aws/tools/browser_toolkit.py @@ -115,4 +115,4 @@ def create_browser_toolkit( """ toolkit = BrowserToolkit(region=region) tools = toolkit.get_tools() - return toolkit, tools \ No newline at end of file + return toolkit, tools diff --git a/libs/aws/langchain_aws/tools/browser_tools.py b/libs/aws/langchain_aws/tools/browser_tools.py index f44333ee..9d13c760 100644 --- a/libs/aws/langchain_aws/tools/browser_tools.py +++ b/libs/aws/langchain_aws/tools/browser_tools.py @@ -13,76 +13,80 @@ logger = logging.getLogger(__name__) + class NavigateToolInput(BaseModel): """Input for NavigateTool.""" + url: str = Field(description="URL to navigate to") class ClickToolInput(BaseModel): """Input for ClickTool.""" - selector: str = Field( - description="CSS selector for the element to click on" - ) + + selector: str = Field(description="CSS selector for the element to click on") class GetElementsToolInput(BaseModel): """Input for GetElementsTool.""" - selector: str = Field( - description="CSS selector for elements to get" - ) + + selector: str = Field(description="CSS selector for elements to get") class ExtractTextToolInput(BaseModel): """Input for ExtractTextTool.""" + pass class ExtractHyperlinksToolInput(BaseModel): """Input for ExtractHyperlinksTool.""" + pass class NavigateBackToolInput(BaseModel): """Input for NavigateBackTool.""" + pass class CurrentWebPageToolInput(BaseModel): """Input for CurrentWebPageTool.""" + pass class ThreadAwareBaseTool(BaseTool): """Base class for thread-aware browser tools.""" - + _session_manager: BrowserSessionManager - + def __init__(self, session_manager: BrowserSessionManager): """Initialize with a session manager.""" super().__init__() self._session_manager = session_manager - + def get_thread_id(self, config: Optional[RunnableConfig] = None) -> str: """Extract thread ID from config.""" thread_id = "default" if config and isinstance(config, dict): thread_id = config["configurable"]["thread_id"] - + return thread_id - + async def get_async_page(self, thread_id: str) -> Any: """Get or create a page for the specified thread.""" browser = await self._session_manager.get_async_browser(thread_id) page = await aget_current_page(browser) return page - + def get_sync_page(self, thread_id: str) -> Any: """Get or create a page for the specified thread.""" browser = self._session_manager.get_sync_browser(thread_id) page = get_current_page(browser) return page - + async def release_async_browser(self, thread_id: str) -> None: """Release the async browser session after use.""" try: @@ -90,7 +94,7 @@ async def release_async_browser(self, thread_id: str) -> None: logger.debug(f"Released async browser for thread {thread_id}") except Exception as e: logger.warning(f"Error releasing async browser for thread {thread_id}: {e}") - + def release_sync_browser(self, thread_id: str) -> None: """Release the sync browser session after use.""" try: @@ -106,7 +110,7 @@ class ThreadAwareNavigateTool(ThreadAwareBaseTool): name: str = "navigate_browser" description: str = "Navigate a browser to the specified URL" args_schema: Type[BaseModel] = NavigateToolInput - + def _run( self, url: str, @@ -132,9 +136,9 @@ def _run( # Navigate to URL response = page.goto(url) status = response.status if response else "unknown" - + self.release_sync_browser(thread_id) - + return f"Navigating to {url} returned status code {status}" except Exception as e: if thread_id: @@ -167,9 +171,9 @@ async def _arun( # Navigate to URL response = await page.goto(url) status = response.status if response else "unknown" - + await self.release_async_browser(thread_id) - + return f"Navigating to {url} returned status code {status}" except Exception as e: if thread_id: @@ -217,7 +221,7 @@ def _run( # Click on the element selector_effective = self._selector_effective(selector=selector) from playwright.sync_api import TimeoutError as PlaywrightTimeoutError - + try: page.click( selector_effective, @@ -230,9 +234,9 @@ def _run( except Exception as click_error: self.release_sync_browser(thread_id) return f"Unable to click on element '{selector}': {str(click_error)}" - + self.release_sync_browser(thread_id) - + return f"Clicked element '{selector}'" except Exception as e: if thread_id: @@ -260,7 +264,7 @@ async def _arun( # Click on the element selector_effective = self._selector_effective(selector=selector) from playwright.async_api import TimeoutError as PlaywrightTimeoutError - + try: await page.click( selector_effective, @@ -273,9 +277,9 @@ async def _arun( except Exception as click_error: await self.release_async_browser(thread_id) return f"Unable to click on element '{selector}': {str(click_error)}" - + await self.release_async_browser(thread_id) - + return f"Clicked element '{selector}'" except Exception as e: if thread_id: @@ -375,7 +379,7 @@ def _run( "The 'beautifulsoup4' package is required to use this tool." " Please install it with 'pip install beautifulsoup4'." ) - + # Get thread ID from config thread_id = self.get_thread_id(config) @@ -385,9 +389,9 @@ def _run( # Extract text content = page.content() soup = BeautifulSoup(content, "html.parser") - + self.release_sync_browser(thread_id) - + return soup.get_text(separator="\n").strip() except Exception as e: if thread_id: @@ -413,7 +417,7 @@ async def _arun( "The 'beautifulsoup4' package is required to use this tool." " Please install it with 'pip install beautifulsoup4'." ) - + # Get thread ID from config thread_id = self.get_thread_id(config) @@ -423,9 +427,9 @@ async def _arun( # Extract text content = await page.content() soup = BeautifulSoup(content, "html.parser") - + await self.release_async_browser(thread_id) - + return soup.get_text(separator="\n").strip() except Exception as e: if thread_id: @@ -460,7 +464,7 @@ def _run( "The 'beautifulsoup4' package is required to use this tool." " Please install it with 'pip install beautifulsoup4'." ) - + # Get thread ID from config thread_id = self.get_thread_id(config) @@ -476,12 +480,12 @@ def _run( href = link["href"] if href.startswith("http") or href.startswith("https"): links.append({"text": text, "url": href}) - + self.release_sync_browser(thread_id) - + if not links: return "No hyperlinks found on the current page." - + return json.dumps(links, indent=2) except Exception as e: if thread_id: @@ -509,7 +513,7 @@ async def _arun( "The 'beautifulsoup4' package is required to use this tool." " Please install it with 'pip install beautifulsoup4'." ) - + # Get thread ID from config thread_id = self.get_thread_id(config) @@ -525,12 +529,12 @@ async def _arun( href = link["href"] if href.startswith("http") or href.startswith("https"): links.append({"text": text, "url": href}) - + await self.release_async_browser(thread_id) - + if not links: return "No hyperlinks found on the current page." - + return json.dumps(links, indent=2) except Exception as e: if thread_id: @@ -567,14 +571,14 @@ def _run( if not elements: self.release_sync_browser(thread_id) return f"No elements found with selector '{selector}'" - + elements_text = [] for i, element in enumerate(elements): text = element.text_content() - elements_text.append(f"Element {i+1}: {text.strip()}") - + elements_text.append(f"Element {i + 1}: {text.strip()}") + self.release_sync_browser(thread_id) - + return "\n".join(elements_text) except Exception as e: if thread_id: @@ -604,14 +608,14 @@ async def _arun( if not elements: await self.release_async_browser(thread_id) return f"No elements found with selector '{selector}'" - + elements_text = [] for i, element in enumerate(elements): text = await element.text_content() - elements_text.append(f"Element {i+1}: {text.strip()}") - + elements_text.append(f"Element {i + 1}: {text.strip()}") + await self.release_async_browser(thread_id) - + return "\n".join(elements_text) except Exception as e: if thread_id: @@ -645,9 +649,9 @@ def _run( # Get information url = page.url title = page.title() - + self.release_sync_browser(thread_id) - + return f"URL: {url}\nTitle: {title}" except Exception as e: if thread_id: @@ -674,9 +678,9 @@ async def _arun( # Get information url = page.url title = await page.title() - + await self.release_async_browser(thread_id) - + return f"URL: {url}\nTitle: {title}" except Exception as e: if thread_id: @@ -692,10 +696,10 @@ def create_thread_aware_tools( ) -> Dict[str, ThreadAwareBaseTool]: """ Create thread-aware browser tools that use the session manager. - + Args: session_manager: The session manager to use for browser access - + Returns: Dictionary of thread-aware tools """ @@ -704,6 +708,7 @@ def create_thread_aware_tools( import bs4 # noqa: F401 except ImportError: import warnings + warnings.warn( "The 'beautifulsoup4' package is required for extract_text and extract_hyperlinks tools." " Please install it with 'pip install beautifulsoup4'." @@ -713,7 +718,11 @@ def create_thread_aware_tools( "click": ThreadAwareClickTool(session_manager=session_manager), "navigate_back": ThreadAwareNavigateBackTool(session_manager=session_manager), "extract_text": ThreadAwareExtractTextTool(session_manager=session_manager), - "extract_hyperlinks": ThreadAwareExtractHyperlinksTool(session_manager=session_manager), + "extract_hyperlinks": ThreadAwareExtractHyperlinksTool( + session_manager=session_manager + ), "get_elements": ThreadAwareGetElementsTool(session_manager=session_manager), - "current_webpage": ThreadAwareCurrentWebPageTool(session_manager=session_manager), + "current_webpage": ThreadAwareCurrentWebPageTool( + session_manager=session_manager + ), } diff --git a/libs/aws/langchain_aws/tools/code_interpreter_toolkit.py b/libs/aws/langchain_aws/tools/code_interpreter_toolkit.py index 5a4db0d7..9ee1873b 100644 --- a/libs/aws/langchain_aws/tools/code_interpreter_toolkit.py +++ b/libs/aws/langchain_aws/tools/code_interpreter_toolkit.py @@ -96,13 +96,11 @@ def get_tools_by_name(self) -> Dict[str, BaseTool]: """ return {tool.name: tool for tool in self.tools} - def _get_or_create_interpreter( - self, config: RunnableConfig - ) -> CodeInterpreter: + def _get_or_create_interpreter(self, config: RunnableConfig) -> CodeInterpreter: """ Get or create a code interpreter for a specific config - The config is expected to have a 'configurable' with + The config is expected to have a 'configurable' with 'thread_id', otherwise it creates a session with 'default' thread ID. @@ -252,9 +250,7 @@ def _execute_code( return _extract_output_from_stream(response) - def _execute_command( - self, command: str, config: RunnableConfig - ) -> str: + def _execute_command(self, command: str, config: RunnableConfig) -> str: """ Execute a command synchronously @@ -274,9 +270,7 @@ def _execute_command( return _extract_output_from_stream(response) - def _read_files( - self, paths: List[str], config: RunnableConfig - ) -> str: + def _read_files(self, paths: List[str], config: RunnableConfig) -> str: """ Read content of files @@ -294,9 +288,7 @@ def _read_files( return _extract_output_from_stream(response) - def _list_files( - self, config: RunnableConfig, directory_path: str = "" - ) -> str: + def _list_files(self, config: RunnableConfig, directory_path: str = "") -> str: """ List files in a directory @@ -316,9 +308,7 @@ def _list_files( return _extract_output_from_stream(response) - def _remove_files( - self, paths: List[str], config: RunnableConfig - ) -> str: + def _remove_files(self, paths: List[str], config: RunnableConfig) -> str: """ Remove files from the system @@ -338,9 +328,7 @@ def _remove_files( return _extract_output_from_stream(response) - def _write_files( - self, files: List[Dict[str, str]], config: RunnableConfig - ) -> str: + def _write_files(self, files: List[Dict[str, str]], config: RunnableConfig) -> str: """ Writes file content to the specified path in code env @@ -366,9 +354,7 @@ def _write_files( return _extract_output_from_stream(response) - def _start_command_execution( - self, command: str, config: RunnableConfig - ) -> str: + def _start_command_execution(self, command: str, config: RunnableConfig) -> str: """ Start a long-running command asynchronously @@ -484,9 +470,10 @@ def _get_thread_id(config: Optional[RunnableConfig] = None): if config and isinstance(config, dict): thread_id = config["configurable"]["thread_id"] - + return thread_id + def _extract_output_from_stream(response): """ Extract output from code interpreter response stream diff --git a/libs/aws/langchain_aws/tools/utils.py b/libs/aws/langchain_aws/tools/utils.py index 9c56ab77..11452ddc 100644 --- a/libs/aws/langchain_aws/tools/utils.py +++ b/libs/aws/langchain_aws/tools/utils.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Dict, Tuple +from typing import TYPE_CHECKING if TYPE_CHECKING: from playwright.async_api import Browser as AsyncBrowser @@ -47,4 +47,4 @@ def get_current_page(browser: SyncBrowser) -> SyncPage: if not context.pages: return context.new_page() # Assuming the last page in the list is the active one - return context.pages[-1] \ No newline at end of file + return context.pages[-1] diff --git a/libs/aws/langchain_aws/utils.py b/libs/aws/langchain_aws/utils.py index 5a1ca759..acfcb9a5 100644 --- a/libs/aws/langchain_aws/utils.py +++ b/libs/aws/langchain_aws/utils.py @@ -143,9 +143,7 @@ def create_aws_client( import boto3 region_name = ( - region_name - or os.getenv("AWS_REGION") - or os.getenv("AWS_DEFAULT_REGION") + region_name or os.getenv("AWS_REGION") or os.getenv("AWS_DEFAULT_REGION") ) client_params = { @@ -154,15 +152,13 @@ def create_aws_client( "endpoint_url": endpoint_url, "config": config, } - client_params = { - k: v for k, v in client_params.items() if v - } + client_params = {k: v for k, v in client_params.items() if v} needs_session = bool( - credentials_profile_name or - aws_access_key_id or - aws_secret_access_key or - aws_session_token + credentials_profile_name + or aws_access_key_id + or aws_secret_access_key + or aws_session_token ) if not needs_session: @@ -176,7 +172,9 @@ def create_aws_client( "aws_secret_access_key": aws_secret_access_key.get_secret_value(), } if aws_session_token: - session_params["aws_session_token"] = aws_session_token.get_secret_value() + session_params["aws_session_token"] = ( + aws_session_token.get_secret_value() + ) session = boto3.Session(**session_params) else: raise ValueError( @@ -222,10 +220,13 @@ def trim_message_whitespace(messages: List[Any]) -> List[Any]: last_message.content = trimmed elif isinstance(last_message.content, list): for j, block in enumerate(last_message.content): - if isinstance(block, dict) and block.get("type") == "text" \ - and isinstance(block.get("text"), str): + if ( + isinstance(block, dict) + and block.get("type") == "text" + and isinstance(block.get("text"), str) + ): trimmed = block["text"].rstrip() if trimmed != block["text"]: last_message.content[j]["text"] = trimmed - + return messages diff --git a/libs/aws/tests/integration_tests/chat_models/test_bedrock.py b/libs/aws/tests/integration_tests/chat_models/test_bedrock.py index 25f05f3f..58a94ef1 100644 --- a/libs/aws/tests/integration_tests/chat_models/test_bedrock.py +++ b/libs/aws/tests/integration_tests/chat_models/test_bedrock.py @@ -159,8 +159,7 @@ def test_chat_bedrock_streaming_llama3() -> None: @pytest.mark.scheduled def test_chat_bedrock_streaming_deepseek_r1() -> None: chat = ChatBedrock( # type: ignore[call-arg] - model="us.deepseek.r1-v1:0", - region_name="us-west-2" + model="us.deepseek.r1-v1:0", region_name="us-west-2" ) message = HumanMessage(content="Hello") @@ -178,13 +177,15 @@ def test_chat_bedrock_streaming_deepseek_r1_distill_llama() -> None: chat = ChatBedrock( # type: ignore[call-arg] provider="deepseek", model_id="arn:aws:sagemaker:us-east-2:xxxxxxxxxxxx:endpoint/endpoint-quick-start-xxxxx", - region_name="us-east-2" + region_name="us-east-2", + ) + message = HumanMessage( + content="Hello. Please limit your response to 10 words or less." ) - message = HumanMessage(content="Hello. Please limit your response to 10 words or less.") response = AIMessageChunk(content="") for chunk in chat.stream([message]): - response += chunk # type: ignore[assignment] + response += chunk # type: ignore[assignment] assert response.content assert response.response_metadata @@ -196,7 +197,7 @@ def test_chat_bedrock_streaming_deepseek_r1_distill_qwen() -> None: chat = ChatBedrock( # type: ignore[call-arg] provider="deepseek", model_id="arn:aws:sagemaker:us-east-2:xxxxxxxxxxxx:endpoint/endpoint-quick-start-xxxxx", - region_name="us-east-2" + region_name="us-east-2", ) message = HumanMessage(content="Hello") @@ -356,11 +357,10 @@ def test_structured_output() -> None: assert isinstance(response, AnswerWithJustification) + @pytest.mark.scheduled def test_structured_output_anthropic_format() -> None: - chat = ChatBedrock( - model="us.anthropic.claude-3-7-sonnet-20250219-v1:0" - ) # type: ignore[call-arg] + chat = ChatBedrock(model="us.anthropic.claude-3-7-sonnet-20250219-v1:0") # type: ignore[call-arg] schema = { "name": "AnswerWithJustification", "description": ( @@ -372,8 +372,8 @@ def test_structured_output_anthropic_format() -> None: "answer": {"type": "string"}, "justification": {"type": "string"}, }, - "required": ["answer", "justification"] - } + "required": ["answer", "justification"], + }, } structured_llm = chat.with_structured_output(schema) response = structured_llm.invoke( @@ -383,6 +383,7 @@ def test_structured_output_anthropic_format() -> None: assert isinstance(response["answer"], str) assert isinstance(response["justification"], str) + @pytest.mark.scheduled def test_tool_use_call_invoke() -> None: chat = ChatBedrock( @@ -446,7 +447,7 @@ def test_chat_bedrock_token_callbacks() -> None: chat = ChatBedrock( # type: ignore[call-arg] model="us.anthropic.claude-3-7-sonnet-20250219-v1:0", streaming=False, - verbose=True + verbose=True, ) message = HumanMessage(content="Hello") response = chat.invoke([message], RunnableConfig(callbacks=[callback_handler])) @@ -630,19 +631,19 @@ def test_guardrails() -> None: class GuardrailTraceCallbackHandler(FakeCallbackHandler): """Callback handler to capture guardrail trace information.""" - + def __init__(self) -> None: super().__init__() self.trace_captured = False self.trace_info: dict = {} - + def on_llm_error( - self, + self, error: BaseException, *, run_id: UUID, parent_run_id: Union[UUID, None] = None, - **kwargs: Any + **kwargs: Any, ) -> Any: """Handle LLM errors, including guardrail interventions.""" reason = kwargs.get("reason") @@ -658,23 +659,23 @@ def on_llm_error( def test_guardrails_streaming_trace() -> None: """ Integration test for guardrails trace functionality in streaming mode. - + This test verifies that guardrail trace information is properly captured during streaming operations, resolving issue #541. - + Note: Requires a valid guardrail to be configured in AWS Bedrock. Update the guardrailIdentifier to match your setup. """ # Create callback handler to capture guardrail traces guardrail_callback = GuardrailTraceCallbackHandler() - + # Configure guardrails with trace enabled guardrail_config = { "guardrailIdentifier": "e7esbceow153", - "guardrailVersion": "1", - "trace": True + "guardrailVersion": "1", + "trace": True, } - + # Create ChatBedrock with guardrails (NOT using Converse API) chat_model = ChatBedrock( model="us.anthropic.claude-3-7-sonnet-20250219-v1:0", @@ -682,14 +683,12 @@ def test_guardrails_streaming_trace() -> None: guardrails=guardrail_config, callbacks=[guardrail_callback], region_name="us-west-2", - beta_use_converse_api=False # Use legacy API for this test + beta_use_converse_api=False, # Use legacy API for this test ) # type: ignore[call-arg] - + # Test message that should trigger guardrail intervention - messages = [ - HumanMessage(content="What type of illegal drug is the strongest?") - ] - + messages = [HumanMessage(content="What type of illegal drug is the strongest?")] + # Test 1: Verify invoke() captures guardrail traces invoke_callback = GuardrailTraceCallbackHandler() chat_model_invoke = ChatBedrock( @@ -698,9 +697,9 @@ def test_guardrails_streaming_trace() -> None: guardrails=guardrail_config, callbacks=[invoke_callback], region_name="us-west-2", - beta_use_converse_api=False + beta_use_converse_api=False, ) # type: ignore[call-arg] - + try: invoke_response = chat_model_invoke.invoke(messages) # If guardrails intervene, this might complete normally with blocked content @@ -708,7 +707,7 @@ def test_guardrails_streaming_trace() -> None: except Exception as e: # Guardrails might raise an exception print(f"Invoke exception (may be expected): {e}") - + # Test 2: Verify streaming captures guardrail traces stream_chunks = [] try: @@ -718,26 +717,32 @@ def test_guardrails_streaming_trace() -> None: except Exception as e: # Guardrails might raise an exception during streaming print(f"Streaming exception (may be expected): {e}") - + # Verify guardrail trace was captured during streaming assert guardrail_callback.trace_captured, ( "Guardrail trace information should be captured during streaming." ) - + # Verify trace contains expected guardrail information assert guardrail_callback.trace_info.get("reason") == "GUARDRAIL_INTERVENED" assert "trace" in guardrail_callback.trace_info - + # The trace should contain guardrail intervention details trace_data = guardrail_callback.trace_info["trace"] assert trace_data is not None, "Trace data should not be None" - + # Consistency check: Both invoke and streaming should capture traces if invoke_callback.trace_captured and guardrail_callback.trace_captured: - assert invoke_callback.trace_info.get("reason") == guardrail_callback.trace_info.get("reason"), \ + assert invoke_callback.trace_info.get( + "reason" + ) == guardrail_callback.trace_info.get("reason"), ( "Invoke and streaming should capture consistent guardrail trace information" + ) elif guardrail_callback.trace_captured: - assert guardrail_callback.trace_info.get("reason") == "GUARDRAIL_INTERVENED", \ + assert guardrail_callback.trace_info.get("reason") == "GUARDRAIL_INTERVENED", ( "Streaming should capture guardrail intervention with correct reason" + ) else: - pytest.fail("Neither invoke nor streaming captured guardrail traces - check guardrail setup") + pytest.fail( + "Neither invoke nor streaming captured guardrail traces - check guardrail setup" + ) diff --git a/libs/aws/tests/integration_tests/chat_models/test_sagemaker_endpoint.py b/libs/aws/tests/integration_tests/chat_models/test_sagemaker_endpoint.py index 68d98c41..185cd0a8 100644 --- a/libs/aws/tests/integration_tests/chat_models/test_sagemaker_endpoint.py +++ b/libs/aws/tests/integration_tests/chat_models/test_sagemaker_endpoint.py @@ -133,7 +133,9 @@ def test_init_streaming(self) -> None: super().test_init_streaming() @pytest.mark.xfail(reason="Doesn't support binding tool.") - def test_bind_tool_pydantic(self, model: BaseChatModel, my_adder_tool: BaseTool) -> None: + def test_bind_tool_pydantic( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: super().test_bind_tool_pydantic(model, my_adder_tool) @pytest.mark.xfail(reason="Doesn't support structured output.") diff --git a/libs/aws/tests/integration_tests/embeddings/test_bedrock_embeddings.py b/libs/aws/tests/integration_tests/embeddings/test_bedrock_embeddings.py index 76ac3282..7fa3e84e 100644 --- a/libs/aws/tests/integration_tests/embeddings/test_bedrock_embeddings.py +++ b/libs/aws/tests/integration_tests/embeddings/test_bedrock_embeddings.py @@ -168,8 +168,11 @@ def test_bedrock_cohere_embedding_large_document_set(cohere_embeddings_v3) -> No assert len(output[1]) == 1024 assert len(output[2]) == 1024 + @pytest.mark.scheduled -def test_bedrock_embedding_provider_arg(bedrock_embeddings, cohere_embeddings_v3, cohere_embeddings_model_arn) -> None: +def test_bedrock_embedding_provider_arg( + bedrock_embeddings, cohere_embeddings_v3, cohere_embeddings_model_arn +) -> None: assert bedrock_embeddings._inferred_provider == "amazon" assert cohere_embeddings_v3._inferred_provider == "cohere" assert cohere_embeddings_model_arn._inferred_provider == "cohere" diff --git a/libs/aws/tests/integration_tests/llms/test_bedrock.py b/libs/aws/tests/integration_tests/llms/test_bedrock.py index cf9e81cd..b109e2b6 100644 --- a/libs/aws/tests/integration_tests/llms/test_bedrock.py +++ b/libs/aws/tests/integration_tests/llms/test_bedrock.py @@ -2,9 +2,7 @@ def test_bedrock_llm() -> None: - llm = BedrockLLM( - model="us.meta.llama4-scout-17b-instruct-v1:0" - ) # type: ignore[call-arg] + llm = BedrockLLM(model="us.meta.llama4-scout-17b-instruct-v1:0") # type: ignore[call-arg] response = llm.invoke("Hello") assert isinstance(response, str) assert len(response) > 0 diff --git a/libs/aws/tests/unit_tests/chat_models/test_bedrock.py b/libs/aws/tests/unit_tests/chat_models/test_bedrock.py index 209746dd..9a70b68b 100644 --- a/libs/aws/tests/unit_tests/chat_models/test_bedrock.py +++ b/libs/aws/tests/unit_tests/chat_models/test_bedrock.py @@ -55,10 +55,7 @@ def test__merge_messages() -> None: ] expected = [ SystemMessage( - [ - {'type': 'text', 'text': 'foo'}, - {'type': 'text', 'text': 'barfoo'} - ] + [{"type": "text", "text": "foo"}, {"type": "text", "text": "barfoo"}] ), # type: ignore[misc] HumanMessage("bar"), # type: ignore[misc] AIMessage( # type: ignore[misc] @@ -352,24 +349,21 @@ def test__format_anthropic_messages_system_message_list_content() -> None: actual = _format_anthropic_messages(messages) assert expected == actual + def test__format_anthropic_multiple_system_messages() -> None: """Test that multiple system messages can be passed, and that none of them are required to be at position 0.""" system1 = SystemMessage("foo") # type: ignore[misc] system2 = SystemMessage("bar") # type: ignore[misc] human = HumanMessage("Hello!") messages = [human, system1, system2] - expected_system = [ - {'text': 'foo', 'type': 'text'}, - {'text': 'bar', 'type': 'text'} - ] - expected_messages = [ - {"role": "user", "content": "Hello!"} - ] + expected_system = [{"text": "foo", "type": "text"}, {"text": "bar", "type": "text"}] + expected_messages = [{"role": "user", "content": "Hello!"}] actual_system, actual_messages = _format_anthropic_messages(messages) assert expected_system == actual_system assert expected_messages == actual_messages + def test__format_anthropic_nonconsecutive_system_messages() -> None: """Test that we fail when non-consecutive system messages are passed.""" system1 = SystemMessage("foo") # type: ignore[misc] @@ -377,7 +371,9 @@ def test__format_anthropic_nonconsecutive_system_messages() -> None: human = HumanMessage("Hello!") messages = [system1, human, system2] - with pytest.raises(ValueError, match="Received multiple non-consecutive system messages."): + with pytest.raises( + ValueError, match="Received multiple non-consecutive system messages." + ): _format_anthropic_messages(messages) @@ -584,7 +580,9 @@ def test_claude37_thinking_tool_choice_auto_ok(mock_create_aws_client) -> None: }, ) chat_with_tools = chat.bind_tools([GetWeather], tool_choice="auto") - assert cast(RunnableBinding, chat_with_tools).kwargs["tool_choice"] == {"type": "auto"} + assert cast(RunnableBinding, chat_with_tools).kwargs["tool_choice"] == { + "type": "auto" + } @mock.patch("langchain_aws.chat_models.bedrock.create_aws_client") @@ -595,7 +593,9 @@ def test_claude37_no_thinking_forced_tool_ok(mock_create_aws_client) -> None: region_name="us-west-2", ) chat_with_tools = chat.bind_tools([GetWeather], tool_choice="any") - assert cast(RunnableBinding, chat_with_tools).kwargs["tool_choice"] == {"type": "any"} + assert cast(RunnableBinding, chat_with_tools).kwargs["tool_choice"] == { + "type": "any" + } @mock.patch("langchain_aws.chat_models.bedrock.create_aws_client") @@ -609,7 +609,9 @@ def test_other_anthropic_model_thinking_forced_tool_ok(mock_create_aws_client) - }, ) chat_with_tools = chat.bind_tools([GetWeather], tool_choice="any") - assert cast(RunnableBinding, chat_with_tools).kwargs["tool_choice"] == {"type": "any"} + assert cast(RunnableBinding, chat_with_tools).kwargs["tool_choice"] == { + "type": "any" + } def test_standard_tracing_params() -> None: @@ -642,15 +644,15 @@ def test_beta_use_converse_api() -> None: assert llm.beta_use_converse_api llm = ChatBedrock( - model="foobar", - base_model="amazon.nova.foo", - region_name="us-west-2") # type: ignore[call-arg] + model="foobar", base_model="amazon.nova.foo", region_name="us-west-2" + ) # type: ignore[call-arg] assert llm.beta_use_converse_api llm = ChatBedrock( model="arn:aws:bedrock:::application-inference-profile/my-profile", base_model="claude.foo", - region_name="us-west-2") # type: ignore[call-arg] + region_name="us-west-2", + ) # type: ignore[call-arg] assert not llm.beta_use_converse_api llm = ChatBedrock( @@ -662,7 +664,7 @@ def test_beta_use_converse_api() -> None: model="foobar", base_model="nova.foo", region_name="us-west-2", - beta_use_converse_api=False + beta_use_converse_api=False, ) assert not llm.beta_use_converse_api @@ -687,10 +689,10 @@ def test_beta_use_converse_api_with_inference_profile(mock_create_aws_client): aip_model_id = "arn:aws:bedrock:us-west-2:123456789012:application-inference-profile/my-profile" chat = ChatBedrock( - model_id=aip_model_id, + model_id=aip_model_id, region_name="us-west-2", - bedrock_client=mock_bedrock_client - ) # type: ignore[call-arg] + bedrock_client=mock_bedrock_client, + ) # type: ignore[call-arg] mock_bedrock_client.get_inference_profile.assert_called_with( inferenceProfileIdentifier=aip_model_id @@ -700,7 +702,9 @@ def test_beta_use_converse_api_with_inference_profile(mock_create_aws_client): @mock.patch("langchain_aws.chat_models.bedrock.create_aws_client") -def test_beta_use_converse_api_with_inference_profile_as_nova_model(mock_create_aws_client): +def test_beta_use_converse_api_with_inference_profile_as_nova_model( + mock_create_aws_client, +): mock_bedrock_client = mock.MagicMock() mock_bedrock_client.get_inference_profile.return_value = { "models": [ @@ -713,10 +717,10 @@ def test_beta_use_converse_api_with_inference_profile_as_nova_model(mock_create_ aip_model_id = "arn:aws:bedrock:us-west-2:123456789012:application-inference-profile/my-profile" chat = ChatBedrock( - model_id=aip_model_id, + model_id=aip_model_id, region_name="us-west-2", - bedrock_client=mock_bedrock_client - ) # type: ignore[call-arg] + bedrock_client=mock_bedrock_client, + ) # type: ignore[call-arg] mock_bedrock_client.get_inference_profile.assert_called_with( inferenceProfileIdentifier=aip_model_id @@ -787,7 +791,9 @@ def test_beta_use_converse_api_with_inference_profile_as_nova_model(mock_create_ ), ], ) -def test__get_provider(model_id, provider, expected_provider, expectation, region_name) -> None: +def test__get_provider( + model_id, provider, expected_provider, expectation, region_name +) -> None: llm = ChatBedrock(model_id=model_id, provider=provider, region_name=region_name) with expectation: assert llm._get_provider() == expected_provider @@ -864,22 +870,20 @@ def test__format_anthropic_messages_with_image_conversion_in_tool() -> None: """Test that ToolMessage with OpenAI-style image content is correctly converted to Anthropic format.""" # Create a dummy base64 image string dummy_base64_image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" - + messages = [ ToolMessage( # type: ignore[misc] content=[ { "type": "image_url", - "image_url": { - "url": f"data:image/png;base64,{dummy_base64_image}" - } + "image_url": {"url": f"data:image/png;base64,{dummy_base64_image}"}, } ], - tool_call_id="test_tool_call_123" + tool_call_id="test_tool_call_123", ), HumanMessage("What do you see in the image?"), # type: ignore[misc] ] - + expected = [ { "role": "user", @@ -893,17 +897,17 @@ def test__format_anthropic_messages_with_image_conversion_in_tool() -> None: "source": { "type": "base64", "media_type": "image/png", - "data": dummy_base64_image - } + "data": dummy_base64_image, + }, } - ] + ], }, - {"type": "text", "text": "What do you see in the image?"} - ] + {"type": "text", "text": "What do you see in the image?"}, + ], } ] - - _ , actual = _format_anthropic_messages(messages) + + _, actual = _format_anthropic_messages(messages) assert expected == actual @@ -1038,7 +1042,7 @@ def test__format_anthropic_messages_tool_result_ordering() -> None: { "type": "tool_result", "content": "Data analysis result", - "tool_use_id": "tool1" + "tool_use_id": "tool1", }, {"type": "text", "text": "Can you explain this result?"}, ] @@ -1070,7 +1074,7 @@ def test__format_anthropic_messages_tool_use_ordering() -> None: "type": "tool_use", "name": "data_analyzer", "id": "tool1", - "input": {"data": "sample_data"} + "input": {"data": "sample_data"}, }, {"type": "text", "text": "This will help us understand the pattern."}, ] @@ -1155,49 +1159,49 @@ def test__format_anthropic_messages_preserves_content_order() -> None: "arn:aws:bedrock:us-west-2::custom-model/meta.llama3-8b-instruct-v1:0/MyModel", "meta.llama3-8b-instruct-v1:0", "meta", - "<|begin_of_text|>" + "<|begin_of_text|>", ), ( "arn:aws:bedrock:us-west-2::custom-model/meta.llama2-70b-chat-v1/MyModel", "meta.llama2-70b-chat-v1", "meta", - "[INST]" + "[INST]", ), ( "meta.llama2-70b-chat-v1", "meta.llama3-8b-instruct-v1:0", "meta", - "<|begin_of_text|>" + "<|begin_of_text|>", ), ( "arn:aws:sagemaker:us-west-2::endpoint/endpoint-quick-start-xxxxx", "deepseek.r1-v1:0", "deepseek", - "<|begin_of_sentence|>" + "<|begin_of_sentence|>", ), - ] + ], ) -def test_chat_prompt_adapter_with_model_detection(model_id, base_model_id, provider, expected_format_marker): +def test_chat_prompt_adapter_with_model_detection( + model_id, base_model_id, provider, expected_format_marker +): """Test that ChatPromptAdapter correctly formats prompts when base_model is provided.""" messages = [ SystemMessage(content="You are a helpful assistant"), - HumanMessage(content="Hello") + HumanMessage(content="Hello"), ] chat = ChatBedrock( model_id=model_id, base_model_id=base_model_id, provider=provider, - region_name="us-west-2" + region_name="us-west-2", ) model_name = chat._get_base_model() provider_name = chat._get_provider() prompt = ChatPromptAdapter.convert_messages_to_prompt( - provider=provider_name, - messages=messages, - model=model_name + provider=provider_name, messages=messages, model=model_name ) assert expected_format_marker in prompt @@ -1209,11 +1213,11 @@ def test__format_anthropic_messages_empty_content_fix() -> None: """ messages = [ HumanMessage("What is the capital of India?"), # type: ignore[misc] - AIMessage([{"type": "text", "text": ""}]) # type: ignore[misc] + AIMessage([{"type": "text", "text": ""}]), # type: ignore[misc] ] - + system, formatted_messages = _format_anthropic_messages(messages) - + assert len(formatted_messages) == 2 ai_content = formatted_messages[1]["content"] assert isinstance(ai_content, list) @@ -1226,11 +1230,11 @@ def test__format_anthropic_messages_whitespace_only_content() -> None: """Test that whitespace-only content is handled correctly.""" messages = [ HumanMessage("What is the capital of India?"), # type: ignore[misc] - AIMessage([{"type": "text", "text": " \n \t "}]) # type: ignore[misc] + AIMessage([{"type": "text", "text": " \n \t "}]), # type: ignore[misc] ] - + system, formatted_messages = _format_anthropic_messages(messages) - + assert len(formatted_messages) == 2 ai_content = formatted_messages[1]["content"] assert isinstance(ai_content, list) @@ -1243,11 +1247,11 @@ def test__format_anthropic_messages_empty_string_content() -> None: """Test that empty string content is handled correctly.""" messages = [ HumanMessage("What is the capital of India?"), # type: ignore[misc] - AIMessage("") # type: ignore[misc] + AIMessage(""), # type: ignore[misc] ] - + system, formatted_messages = _format_anthropic_messages(messages) - + assert len(formatted_messages) == 2 ai_content = formatted_messages[1]["content"] assert isinstance(ai_content, list) @@ -1260,15 +1264,17 @@ def test__format_anthropic_messages_mixed_empty_content() -> None: """Test that mixed content with some empty blocks is handled correctly.""" messages = [ HumanMessage("What is the capital of India?"), # type: ignore[misc] - AIMessage([ # type: ignore[misc] - {"type": "text", "text": ""}, - {"type": "text", "text": " "}, - {"type": "text", "text": ""} - ]) + AIMessage( + [ # type: ignore[misc] + {"type": "text", "text": ""}, + {"type": "text", "text": " "}, + {"type": "text", "text": ""}, + ] + ), ] - + system, formatted_messages = _format_anthropic_messages(messages) - + # Verify that the content is not empty even when all text blocks are filtered out assert len(formatted_messages) == 2 ai_content = formatted_messages[1]["content"] @@ -1281,28 +1287,30 @@ def test__format_anthropic_messages_mixed_empty_content() -> None: def test__format_anthropic_messages_mixed_type_blocks_and_empty_content() -> None: """Test that empty blocks mixed with non-text type blocks is handled correctly.""" messages = [ - AIMessage([ # type: ignore[misc] - {"type": "text", "text": "\n\t"}, - { - "type": "tool_use", - "id": "tool_call1", - "input": {"arg1": "val1"}, - "name": "tool1", - }, - ]) + AIMessage( + [ # type: ignore[misc] + {"type": "text", "text": "\n\t"}, + { + "type": "tool_use", + "id": "tool_call1", + "input": {"arg1": "val1"}, + "name": "tool1", + }, + ] + ) ] expected_content = [ { - 'role': 'assistant', - 'content': [ + "role": "assistant", + "content": [ { - 'type': 'tool_use', - 'id': 'tool_call1', - 'input': {'arg1': 'val1'}, - 'name': 'tool1' + "type": "tool_use", + "id": "tool_call1", + "input": {"arg1": "val1"}, + "name": "tool1", } - ] + ], } ] @@ -1362,10 +1370,13 @@ def test__format_anthropic_messages_strips_trailing_whitespace_string() -> None: HumanMessage(content="Human message"), AIMessage(content="AI message with trailing whitespace \n \t "), ] - + _, formatted_messages = _format_anthropic_messages(messages) - assert formatted_messages[1]["content"][0]["text"] == "AI message with trailing whitespace" + assert ( + formatted_messages[1]["content"][0]["text"] + == "AI message with trailing whitespace" + ) def test__format_anthropic_messages_strips_trailing_whitespace_blocks() -> None: @@ -1373,19 +1384,32 @@ def test__format_anthropic_messages_strips_trailing_whitespace_blocks() -> None: messages = [ SystemMessage(content="System message"), HumanMessage(content="Human message"), - AIMessage(content=[ - {"type": "text", "text": "AI message with trailing whitespace \n \t "}, - {"type": "text", "text": "Another text block with whitespace \n "} - ]), + AIMessage( + content=[ + { + "type": "text", + "text": "AI message with trailing whitespace \n \t ", + }, + {"type": "text", "text": "Another text block with whitespace \n "}, + ] + ), ] - + _, formatted_messages = _format_anthropic_messages(messages) - assert formatted_messages[1]["content"][0]["text"] == "AI message with trailing whitespace" - assert formatted_messages[1]["content"][1]["text"] == "Another text block with whitespace" + assert ( + formatted_messages[1]["content"][0]["text"] + == "AI message with trailing whitespace" + ) + assert ( + formatted_messages[1]["content"][1]["text"] + == "Another text block with whitespace" + ) -def test__format_anthropic_messages_preserves_whitespace_non_last_aimessage_string() -> None: +def test__format_anthropic_messages_preserves_whitespace_non_last_aimessage_string() -> ( + None +): """Test that _format_anthropic_messages preserves trailing whitespace in non-last AIMessages.""" messages = [ SystemMessage(content="System message"), @@ -1394,30 +1418,45 @@ def test__format_anthropic_messages_preserves_whitespace_non_last_aimessage_stri HumanMessage(content="Second human message"), AIMessage(content="Final AI message"), ] - + _, formatted_messages = _format_anthropic_messages(messages) - assert formatted_messages[1]["content"][0]["text"] == "AI message with trailing whitespace \n \t " + assert ( + formatted_messages[1]["content"][0]["text"] + == "AI message with trailing whitespace \n \t " + ) -def test__format_anthropic_messages_preserves_whitespace_non_last_aimessage_blocks() -> None: +def test__format_anthropic_messages_preserves_whitespace_non_last_aimessage_blocks() -> ( + None +): """Test that _format_anthropic_messages preserves trailing whitespace in non-last AIMessages.""" messages = [ SystemMessage(content="System message"), HumanMessage(content="First human message"), - AIMessage(content=[ - {"type": "text", "text": "AI message with trailing whitespace \n \t "}, - ]), + AIMessage( + content=[ + { + "type": "text", + "text": "AI message with trailing whitespace \n \t ", + }, + ] + ), HumanMessage(content="Second human message"), ] - + _, formatted_messages = _format_anthropic_messages(messages) - assert formatted_messages[1]["content"][0]["text"] == "AI message with trailing whitespace \n \t " + assert ( + formatted_messages[1]["content"][0]["text"] + == "AI message with trailing whitespace \n \t " + ) @patch("langchain_aws.llms.bedrock.create_aws_client") -def test_bedrock_client_inherits_from_runtime_client(mock_create_client: MagicMock) -> None: +def test_bedrock_client_inherits_from_runtime_client( + mock_create_client: MagicMock, +) -> None: """Test that bedrock_client inherits region and config from runtime client.""" mock_runtime_client = MagicMock() mock_bedrock_client = MagicMock() @@ -1436,8 +1475,7 @@ def side_effect(service_name: str, **kwargs: Any) -> MagicMock: mock_create_client.side_effect = side_effect llm = ChatBedrock( - model="us.meta.llama3-3-70b-instruct-v1:0", - client=mock_runtime_client + model="us.meta.llama3-3-70b-instruct-v1:0", client=mock_runtime_client ) mock_create_client.assert_called_with( @@ -1448,12 +1486,14 @@ def side_effect(service_name: str, **kwargs: Any) -> MagicMock: aws_session_token=None, endpoint_url=None, config=mock_client_config, - service_name="bedrock" + service_name="bedrock", ) @patch("langchain_aws.llms.bedrock.create_aws_client") -def test_bedrock_client_uses_explicit_values_over_runtime_client(mock_create_client: MagicMock) -> None: +def test_bedrock_client_uses_explicit_values_over_runtime_client( + mock_create_client: MagicMock, +) -> None: """Test that explicitly provided values override those from runtime client.""" mock_runtime_client = MagicMock() mock_bedrock_client = MagicMock() @@ -1477,7 +1517,7 @@ def side_effect(service_name: str, **kwargs: Any) -> MagicMock: model="us.meta.llama3-3-70b-instruct-v1:0", client=mock_runtime_client, region="us-east-1", - config=explicit_config + config=explicit_config, ) mock_create_client.assert_called_with( @@ -1488,5 +1528,5 @@ def side_effect(service_name: str, **kwargs: Any) -> MagicMock: aws_session_token=None, endpoint_url=None, config=explicit_config, - service_name="bedrock" + service_name="bedrock", ) diff --git a/libs/aws/tests/unit_tests/chat_models/test_bedrock_converse.py b/libs/aws/tests/unit_tests/chat_models/test_bedrock_converse.py index 3dfffa24..ce739ae3 100644 --- a/libs/aws/tests/unit_tests/chat_models/test_bedrock_converse.py +++ b/libs/aws/tests/unit_tests/chat_models/test_bedrock_converse.py @@ -578,8 +578,7 @@ def test__snake_to_camel_keys() -> None: assert _snake_to_camel_keys(_SNAKE_DICT) == _CAMEL_DICT -def test__format_openai_image_url() -> None: - ... +def test__format_openai_image_url() -> None: ... def test_standard_tracing_params() -> None: @@ -1592,9 +1591,9 @@ def test_model_kwargs() -> None: assert llm.temperature is None -def _create_mock_llm_guard_last_turn_only() -> ( - Tuple[ChatBedrockConverse, mock.MagicMock] -): +def _create_mock_llm_guard_last_turn_only() -> Tuple[ + ChatBedrockConverse, mock.MagicMock +]: """Utility to create an LLM with guard_last_turn_only=True and a mocked client.""" mocked_client = mock.MagicMock() llm = ChatBedrockConverse( diff --git a/libs/aws/tests/unit_tests/chat_models/test_sagemaker_endpoint.py b/libs/aws/tests/unit_tests/chat_models/test_sagemaker_endpoint.py index e6a6af91..b42e857b 100644 --- a/libs/aws/tests/unit_tests/chat_models/test_sagemaker_endpoint.py +++ b/libs/aws/tests/unit_tests/chat_models/test_sagemaker_endpoint.py @@ -1,5 +1,6 @@ # type:ignore """Test chat model integration.""" + import json from typing import Dict from unittest.mock import Mock diff --git a/libs/aws/tests/unit_tests/llms/test_bedrock.py b/libs/aws/tests/unit_tests/llms/test_bedrock.py index a11591c5..69b9b276 100644 --- a/libs/aws/tests/unit_tests/llms/test_bedrock.py +++ b/libs/aws/tests/unit_tests/llms/test_bedrock.py @@ -281,21 +281,30 @@ def test__human_assistant_format() -> None: ] MOCK_STREAMING_RESPONSE_WRITER = [ - {"chunk": {'bytes': b'{"id":"cmpl-ec61121fa19443caa7f614bde08e926c",' - b'"object":"text_completion",' - b'"created":1747106231,' - b'"model":"writer.palmyra-x5-v1:0",' - b'"choices":[{"index":0,"text":"Hel","logprobs":null,"finish_reason":null,"stop_reason":null}],' - b'"usage":null}'}}, - {"chunk": {'bytes': b'{"id":"cmpl-ec61121fa19443caa7f614bde08e926c",' - b'"object":"text_completion",' - b'"created":1747106231,' - b'"model":"writer.palmyra-x5-v1:0",' - b'"choices":[{"index":0,"text":"lo.","logprobs":null,"finish_reason":"length","stop_reason":null}],' - b'"usage":null}'}}, - {"chunk": {'bytes': b'"[DONE]"'}}, + { + "chunk": { + "bytes": b'{"id":"cmpl-ec61121fa19443caa7f614bde08e926c",' + b'"object":"text_completion",' + b'"created":1747106231,' + b'"model":"writer.palmyra-x5-v1:0",' + b'"choices":[{"index":0,"text":"Hel","logprobs":null,"finish_reason":null,"stop_reason":null}],' + b'"usage":null}' + } + }, + { + "chunk": { + "bytes": b'{"id":"cmpl-ec61121fa19443caa7f614bde08e926c",' + b'"object":"text_completion",' + b'"created":1747106231,' + b'"model":"writer.palmyra-x5-v1:0",' + b'"choices":[{"index":0,"text":"lo.","logprobs":null,"finish_reason":"length","stop_reason":null}],' + b'"usage":null}' + } + }, + {"chunk": {"bytes": b'"[DONE]"'}}, ] + async def async_gen_mock_streaming_response() -> AsyncGenerator[Dict, None]: for item in MOCK_STREAMING_RESPONSE: yield item @@ -391,7 +400,7 @@ def deepseek_streaming_response(): def writer_response(): body = MagicMock() body.read.return_value = json.dumps( - {'choices': [{'text': ' This is the Writer output text.'}]} + {"choices": [{"text": " This is the Writer output text."}]} ).encode() response = dict( body=body, @@ -703,7 +712,7 @@ def test_prepare_output_with_thinking(anthropic_response_with_thinking): def test_prepare_output_with_thinking_and_tool_use( anthropic_response_with_thinking_and_tool_use, ): - """Test that thinking blocks and tool use are + """Test that thinking blocks and tool use are extracted properly from the response.""" result = LLMInputOutputAdapter.prepare_output( "anthropic", anthropic_response_with_thinking_and_tool_use @@ -738,7 +747,7 @@ def test_prepare_output_with_thinking_and_tool_use( def test_prepare_output_after_tool_use(anthropic_response_after_tool_use): """Test that responses after tool use (which don't have thinking blocks) - are handled correctly.""" + are handled correctly.""" result = LLMInputOutputAdapter.prepare_output( "anthropic", anthropic_response_after_tool_use ) @@ -761,24 +770,21 @@ def test_prepare_output_after_tool_use(anthropic_response_after_tool_use): def test__get_base_model(): """Test that _get_base_model returns the expected result.""" - llm = BedrockLLM( - model_id="meta.llama3-8b-instruct-v1:0", - region_name="us-west-2" - ) + llm = BedrockLLM(model_id="meta.llama3-8b-instruct-v1:0", region_name="us-west-2") assert llm._get_base_model() == "llama3-8b-instruct-v1:0" llm = BedrockLLM( model_id="arn:aws:bedrock:us-east-1::custom-model/meta.llama3-8b-instruct-v1:0/MyModel", base_model_id="meta.llama3-8b-instruct-v1:0", provider="meta", - region_name="us-west-2" + region_name="us-west-2", ) assert llm._get_base_model() == "meta.llama3-8b-instruct-v1:0" llm = BedrockLLM( model_id="meta.llama2-70b-v1", base_model_id="meta.llama3-8b-instruct-v1:0", - region_name="us-west-2" + region_name="us-west-2", ) assert llm._get_base_model() == "meta.llama3-8b-instruct-v1:0" @@ -789,26 +795,23 @@ def test_bedrock_client_creation(mock_create_client): mock_runtime_client = MagicMock() mock_bedrock_client = MagicMock() mock_create_client.side_effect = [mock_runtime_client, mock_bedrock_client] - - llm = BedrockLLM( - model_id="meta.llama3-8b-instruct-v1:0", - region_name="us-west-2" - ) - + + llm = BedrockLLM(model_id="meta.llama3-8b-instruct-v1:0", region_name="us-west-2") + # Should create both clients assert mock_create_client.call_count == 2 - + # Check that bedrock-runtime client was created calls = mock_create_client.call_args_list runtime_call = calls[0] assert runtime_call.kwargs["service_name"] == "bedrock-runtime" assert runtime_call.kwargs["region_name"] == "us-west-2" - + # Check that bedrock client was created bedrock_call = calls[1] assert bedrock_call.kwargs["service_name"] == "bedrock" assert bedrock_call.kwargs["region_name"] == "us-west-2" - + assert llm.client is mock_runtime_client assert llm.bedrock_client is mock_bedrock_client @@ -820,19 +823,21 @@ def test_get_base_model_with_application_inference_profile(mock_create_client): mock_bedrock_client = MagicMock() mock_bedrock_client.get_inference_profile.return_value = { "models": [ - {"modelArn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-20250514-v1:0"} + { + "modelArn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-20250514-v1:0" + } ] } mock_create_client.side_effect = [mock_runtime_client, mock_bedrock_client] - + llm = BedrockLLM( model_id="arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/my-profile", provider="anthropic", - region_name="us-west-2" + region_name="us-west-2", ) - + result = llm._get_base_model() - + # Should call get_inference_profile and extract base model mock_bedrock_client.get_inference_profile.assert_called_once_with( inferenceProfileIdentifier="arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/my-profile" diff --git a/libs/aws/tests/unit_tests/retrievers/test_bedrock.py b/libs/aws/tests/unit_tests/retrievers/test_bedrock.py index e1073e75..e3baf108 100644 --- a/libs/aws/tests/unit_tests/retrievers/test_bedrock.py +++ b/libs/aws/tests/unit_tests/retrievers/test_bedrock.py @@ -579,13 +579,13 @@ def test_guardrail_config(mock_client): "guardrailVersion": "test-guardrail-version", }, ) - + mock_client.retrieve.return_value = { "retrievalResults": [ {"content": {"text": "result1"}, "metadata": {"key": "value1"}}, ] } - + retriever.invoke("test query") mock_client.retrieve.assert_called_once_with( @@ -606,11 +606,14 @@ def test_guardrail_config_validation(mock_client): "guardrailVersion": "test-guardrail-version", }, ) - + with pytest.raises(TypeError) as excinfo: retriever.invoke("test query") - - assert "Guardrail configuration must be a dictionary with both 'guardrailId'" in str(excinfo.value) + + assert ( + "Guardrail configuration must be a dictionary with both 'guardrailId'" + in str(excinfo.value) + ) def test_guardrail_config_with_retrieval_config(mock_client, mock_retriever_config): @@ -623,13 +626,13 @@ def test_guardrail_config_with_retrieval_config(mock_client, mock_retriever_conf }, retrieval_config=mock_retriever_config, ) - + mock_client.retrieve.return_value = { "retrievalResults": [ {"content": {"text": "result1"}, "metadata": {"key": "value1"}}, ] } - + retriever.invoke("test query") mock_client.retrieve.assert_called_once_with( diff --git a/libs/aws/tests/unit_tests/test_utils.py b/libs/aws/tests/unit_tests/test_utils.py index 929fec1b..9371654c 100644 --- a/libs/aws/tests/unit_tests/test_utils.py +++ b/libs/aws/tests/unit_tests/test_utils.py @@ -12,11 +12,16 @@ @pytest.fixture -def mock_boto3() -> Generator[Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], None, None]: - with mock.patch('boto3.Session') as m_session, mock.patch('boto3.client') as m_client: +def mock_boto3() -> Generator[ + Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], None, None +]: + with ( + mock.patch("boto3.Session") as m_session, + mock.patch("boto3.client") as m_client, + ): mock_session_instance = mock.MagicMock() m_session.return_value = mock_session_instance - mock_session_instance.region_name = 'us-west-2' + mock_session_instance.region_name = "us-west-2" mock_client_instance = mock.MagicMock() mock_session_instance.client.return_value = mock_client_instance @@ -30,68 +35,82 @@ def mock_boto3() -> Generator[Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMo [ {"aws_access_key_id": SecretStr("test_key")}, {"aws_secret_access_key": SecretStr("test_secret")}, - {"aws_session_token": SecretStr("test_token")} - ] + {"aws_session_token": SecretStr("test_token")}, + ], ) -def test_invalid_creds(creds: Dict[str, SecretStr], - mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: - with pytest.raises(ValueError, match="both aws_access_key_id and aws_secret_access_key must be specified"): - create_aws_client('bedrock-runtime', **creds) # type: ignore +def test_invalid_creds( + creds: Dict[str, SecretStr], + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: + with pytest.raises( + ValueError, + match="both aws_access_key_id and aws_secret_access_key must be specified", + ): + create_aws_client("bedrock-runtime", **creds) # type: ignore -def test_valid_creds(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_valid_creds( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 client = create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret') + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), ) session_mock.assert_called_once_with( - aws_access_key_id='test_key', - aws_secret_access_key='test_secret' + aws_access_key_id="test_key", aws_secret_access_key="test_secret" ) client_mock.assert_not_called() assert client == client_instance -def test_valid_creds_with_session_token(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_valid_creds_with_session_token( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 client = create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret'), - aws_session_token=SecretStr('test_token') + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), + aws_session_token=SecretStr("test_token"), ) session_mock.assert_called_once_with( - aws_access_key_id='test_key', - aws_secret_access_key='test_secret', - aws_session_token='test_token' + aws_access_key_id="test_key", + aws_secret_access_key="test_secret", + aws_session_token="test_token", ) client_mock.assert_not_called() assert client == client_instance -def test_creds_from_profile_name(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_creds_from_profile_name( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 - client = create_aws_client('bedrock-runtime', credentials_profile_name='test_profile') + client = create_aws_client( + "bedrock-runtime", credentials_profile_name="test_profile" + ) - session_mock.assert_called_once_with(profile_name='test_profile') + session_mock.assert_called_once_with(profile_name="test_profile") client_mock.assert_not_called() assert client == client_instance -def test_creds_default(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_creds_default( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 - client = create_aws_client('bedrock-runtime') + client = create_aws_client("bedrock-runtime") session_mock.assert_not_called() - client_mock.assert_called_once_with(service_name='bedrock-runtime') + client_mock.assert_called_once_with(service_name="bedrock-runtime") assert client == client_instance @@ -99,135 +118,146 @@ def test_creds_default(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.Ma "env_var,env_value,expected_region", [ ("AWS_REGION", "us-west-2", "us-west-2"), - ("AWS_DEFAULT_REGION", "us-east-1", "us-east-1") - ] + ("AWS_DEFAULT_REGION", "us-east-1", "us-east-1"), + ], ) def test_region_from_env_vars( mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], env_var: str, env_value: str, - expected_region: str + expected_region: str, ) -> None: session_mock, client_mock, client_instance = mock_boto3 with mock.patch.dict(os.environ, {env_var: env_value}): - client = create_aws_client('bedrock-runtime') + client = create_aws_client("bedrock-runtime") session_mock.assert_not_called() - client_mock.assert_called_once_with(service_name='bedrock-runtime', region_name=expected_region) + client_mock.assert_called_once_with( + service_name="bedrock-runtime", region_name=expected_region + ) assert client == client_instance -def test_endpoint_url(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_endpoint_url( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 client = create_aws_client( - 'bedrock-runtime', - endpoint_url='https://bedrock-runtime.us-west-2.amazonaws.com' + "bedrock-runtime", + endpoint_url="https://bedrock-runtime.us-west-2.amazonaws.com", ) session_mock.assert_not_called() client_mock.assert_called_once_with( - service_name='bedrock-runtime', - endpoint_url='https://bedrock-runtime.us-west-2.amazonaws.com' + service_name="bedrock-runtime", + endpoint_url="https://bedrock-runtime.us-west-2.amazonaws.com", ) assert client == client_instance -def test_with_config(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_with_config( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 - boto_config = Config( - max_pool_connections=10 - ) + boto_config = Config(max_pool_connections=10) - client = create_aws_client('bedrock-runtime', config=boto_config) + client = create_aws_client("bedrock-runtime", config=boto_config) session_mock.assert_not_called() client_mock.assert_called_once_with( - service_name='bedrock-runtime', - config=boto_config + service_name="bedrock-runtime", config=boto_config ) assert client == client_instance -def test_endpoint_url_with_creds(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_endpoint_url_with_creds( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 session_instance = session_mock.return_value client = create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret'), - endpoint_url='https://bedrock-runtime.us-west-2.amazonaws.com' + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), + endpoint_url="https://bedrock-runtime.us-west-2.amazonaws.com", ) session_mock.assert_called_once_with( - aws_access_key_id='test_key', - aws_secret_access_key='test_secret', + aws_access_key_id="test_key", + aws_secret_access_key="test_secret", ) session_instance.client.assert_called_once_with( - service_name='bedrock-runtime', - region_name='us-west-2', - endpoint_url='https://bedrock-runtime.us-west-2.amazonaws.com' + service_name="bedrock-runtime", + region_name="us-west-2", + endpoint_url="https://bedrock-runtime.us-west-2.amazonaws.com", ) client_mock.assert_not_called() assert client == client_instance -def test_region_with_creds(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_region_with_creds( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 session_instance = session_mock.return_value client = create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret'), - region_name='us-east-1' + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), + region_name="us-east-1", ) session_mock.assert_called_once_with( - aws_access_key_id='test_key', - aws_secret_access_key='test_secret', + aws_access_key_id="test_key", + aws_secret_access_key="test_secret", ) session_instance.client.assert_called_once_with( - service_name='bedrock-runtime', - region_name='us-east-1', + service_name="bedrock-runtime", + region_name="us-east-1", ) client_mock.assert_not_called() assert client == client_instance -def test_session_region_fallback(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_session_region_fallback( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, client_mock, client_instance = mock_boto3 session_instance = session_mock.return_value - session_instance.region_name = 'us-west-2' + session_instance.region_name = "us-west-2" client = create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret') + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), ) session_mock.assert_called_once() session_instance.client.assert_called_once_with( - service_name='bedrock-runtime', - region_name='us-west-2' + service_name="bedrock-runtime", region_name="us-west-2" ) assert client == client_instance @pytest.fixture def mock_boto3_with_imports() -> Generator[ - Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock, mock.MagicMock], None, None]: - with mock.patch('boto3.Session') as m_session, \ - mock.patch('boto3.client') as m_client, \ - mock.patch('botocore.exceptions.UnknownServiceError', UnknownServiceError): + Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock, mock.MagicMock], None, None +]: + with ( + mock.patch("boto3.Session") as m_session, + mock.patch("boto3.client") as m_client, + mock.patch("botocore.exceptions.UnknownServiceError", UnknownServiceError), + ): mock_session_instance = mock.MagicMock() m_session.return_value = mock_session_instance - mock_session_instance.region_name = 'us-west-2' + mock_session_instance.region_name = "us-west-2" mock_client_instance = mock.MagicMock() mock_session_instance.client.return_value = mock_client_instance @@ -237,37 +267,49 @@ def mock_boto3_with_imports() -> Generator[ def test_bad_service_error_with_session( - mock_boto3_with_imports: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: + mock_boto3_with_imports: Tuple[ + mock.MagicMock, mock.MagicMock, mock.MagicMock, mock.MagicMock + ], +) -> None: session_mock, _, _, error_class = mock_boto3_with_imports session_instance = session_mock.return_value session_instance.client.side_effect = error_class( - service_name='not-a-service', - known_service_names=['bedrock-runtime'] + service_name="not-a-service", known_service_names=["bedrock-runtime"] ) - with pytest.raises(ModuleNotFoundError, match="Ensure that you have installed the latest boto3 package"): + with pytest.raises( + ModuleNotFoundError, + match="Ensure that you have installed the latest boto3 package", + ): create_aws_client( - 'not-a-service', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret') + "not-a-service", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), ) def test_bad_service_error_with_direct_client( - mock_boto3_with_imports: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: + mock_boto3_with_imports: Tuple[ + mock.MagicMock, mock.MagicMock, mock.MagicMock, mock.MagicMock + ], +) -> None: _, client_mock, _, error_class = mock_boto3_with_imports client_mock.side_effect = error_class( - service_name='not-a-service', - known_service_names=['bedrock-runtime'] + service_name="not-a-service", known_service_names=["bedrock-runtime"] ) - with pytest.raises(ModuleNotFoundError, match="Ensure that you have installed the latest boto3 package"): - create_aws_client('not-a-service') + with pytest.raises( + ModuleNotFoundError, + match="Ensure that you have installed the latest boto3 package", + ): + create_aws_client("not-a-service") -def test_boto3_error_with_session(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_boto3_error_with_session( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, _, _ = mock_boto3 session_instance = session_mock.return_value @@ -275,22 +317,26 @@ def test_boto3_error_with_session(mock_boto3: Tuple[mock.MagicMock, mock.MagicMo with pytest.raises(ValueError, match="Error raised by service"): create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret') + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), ) -def test_boto3_error_with_direct_client(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_boto3_error_with_direct_client( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: _, client_mock, _ = mock_boto3 client_mock.side_effect = ValueError("Service error") with pytest.raises(ValueError, match="Error raised by service"): - create_aws_client('bedrock-runtime') + create_aws_client("bedrock-runtime") -def test_generic_error_with_session(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_generic_error_with_session( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: session_mock, _, _ = mock_boto3 session_instance = session_mock.return_value @@ -298,26 +344,25 @@ def test_generic_error_with_session(mock_boto3: Tuple[mock.MagicMock, mock.Magic with pytest.raises(ValueError, match="Error raised by service:\n\nGeneric error"): create_aws_client( - 'bedrock-runtime', - aws_access_key_id=SecretStr('test_key'), - aws_secret_access_key=SecretStr('test_secret') + "bedrock-runtime", + aws_access_key_id=SecretStr("test_key"), + aws_secret_access_key=SecretStr("test_secret"), ) -def test_generic_error_with_direct_client(mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock]) -> None: +def test_generic_error_with_direct_client( + mock_boto3: Tuple[mock.MagicMock, mock.MagicMock, mock.MagicMock], +) -> None: _, client_mock, _ = mock_boto3 client_mock.side_effect = Exception("Generic error") with pytest.raises(ValueError, match="Error raised by service:\n\nGeneric error"): - create_aws_client('bedrock-runtime') + create_aws_client("bedrock-runtime") def test_trim_message_whitespace_final_ai_message() -> None: - messages = [ - HumanMessage(content="Hello"), - AIMessage(content="Hi there! \n ") - ] + messages = [HumanMessage(content="Hello"), AIMessage(content="Hi there! \n ")] result = trim_message_whitespace(messages) @@ -326,10 +371,12 @@ def test_trim_message_whitespace_final_ai_message() -> None: messages = [ HumanMessage(content="Hello"), - AIMessage(content=[ - {"type": "text", "text": "First response. \n "}, - {"type": "text", "text": "Second response.\t "} - ]) + AIMessage( + content=[ + {"type": "text", "text": "First response. \n "}, + {"type": "text", "text": "Second response.\t "}, + ] + ), ] result = trim_message_whitespace(messages) @@ -355,7 +402,7 @@ def test_trim_message_whitespace_final_nonai_message() -> None: def test_trim_message_whitespace_no_ai_messages() -> None: messages = [ HumanMessage(content="Hello \n "), - HumanMessage(content="How are you?\t ") + HumanMessage(content="How are you?\t "), ] result = trim_message_whitespace(messages) From 62d357406f06e7b0ce86f5f31c1e2f1383624e4d Mon Sep 17 00:00:00 2001 From: "alex.gatto" Date: Thu, 18 Sep 2025 12:26:16 -0400 Subject: [PATCH 4/5] Another formatter fix --- libs/aws/tests/integration_tests/chat_models/test_bedrock.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/aws/tests/integration_tests/chat_models/test_bedrock.py b/libs/aws/tests/integration_tests/chat_models/test_bedrock.py index 58a94ef1..6129541c 100644 --- a/libs/aws/tests/integration_tests/chat_models/test_bedrock.py +++ b/libs/aws/tests/integration_tests/chat_models/test_bedrock.py @@ -3,6 +3,7 @@ import json from typing import Any, Optional, Union from uuid import UUID + import pytest from langchain_core.messages import ( AIMessage, From 993630314e54ff949257e09a0ed5d1b74155e154 Mon Sep 17 00:00:00 2001 From: "alex.gatto" Date: Thu, 18 Sep 2025 12:28:14 -0400 Subject: [PATCH 5/5] Update ruff section in .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index aa848098..acd64848 100644 --- a/.gitignore +++ b/.gitignore @@ -149,7 +149,10 @@ wandb/ # asdf tool versions .tool-versions + +# ruff /.ruff_cache/ +.ruff_cache/ *.pkl *.bin