diff --git a/.konflux/build-args-konflux.conf b/.konflux/build-args-konflux.conf new file mode 100644 index 000000000..b12b1d8cd --- /dev/null +++ b/.konflux/build-args-konflux.conf @@ -0,0 +1,4 @@ +BUILDER_BASE_IMAGE=quay.io/aipcc/base-images/cpu:3.4.2-1782270165 +BUILDER_DNF_COMMAND=dnf +RUNTIME_BASE_IMAGE=quay.io/aipcc/base-images/cpu:3.4.2-1782270165 +RUNTIME_DNF_COMMAND=dnf diff --git a/.konflux/profiles.toml b/.konflux/profiles.toml new file mode 100644 index 000000000..bf7bca719 --- /dev/null +++ b/.konflux/profiles.toml @@ -0,0 +1,16 @@ +[common] +python_version = "3.12" +platforms = ["x86_64", "aarch64"] +bootstrap_packages = ["maturin"] + +[profiles.cpu] +rhoai_index_url = "https://packages.redhat.com/api/pypi/public-rhai/rhoai/3.4/cpu-ubi9/simple/" +output_suffix = "" +tekton_files = [ + ".tekton/lightspeed-stack-pull-request.yaml", + ".tekton/lightspeed-stack-push.yaml", + ".tekton/lightspeed-stack-0-5-pull-request.yaml", + ".tekton/lightspeed-stack-0-5-push.yaml", + ".tekton/lightspeed-stack-release-0-5-pull-request.yaml", + ".tekton/lightspeed-stack-release-0-5-push.yaml", +] diff --git a/.konflux/pypi_wheel_only.txt b/.konflux/pypi_wheel_only.txt new file mode 100644 index 000000000..c23de23ec --- /dev/null +++ b/.konflux/pypi_wheel_only.txt @@ -0,0 +1,5 @@ +# Packages where PyPI has an sdist but it cannot be built reliably. +# Rust/C++ bootstrap, binary-only distributions, CUDA stack. +# +# Pure-Python packages whose build backends pull in C-extension deps +# (e.g. jinja2→markupsafe) that conflict with RHOAI-rebuilt wheels. diff --git a/redhat.repo b/.konflux/redhat.repo similarity index 100% rename from redhat.repo rename to .konflux/redhat.repo diff --git a/.konflux/requirements-build.txt b/.konflux/requirements-build.txt new file mode 100644 index 000000000..fe6f2c1eb --- /dev/null +++ b/.konflux/requirements-build.txt @@ -0,0 +1,51 @@ +# +# This file is autogenerated by pip-compile with Python 3.13 +# by the following command: +# +# pybuild-deps compile --output-file=.konflux/requirements-build.txt .konflux/_tmp_sdist_list.txt +# +calver==2025.10.20 + # via trove-classifiers +dunamai==1.26.1 + # via uv-dynamic-versioning +flit-core==3.12.0 + # via + # jinja2 + # packaging + # pathspec +hatch-fancy-pypi-readme==25.1.0 + # via llama-stack-client +hatchling==1.26.3 + # via + # hatch-fancy-pypi-readme + # llama-stack-client +hatchling==1.30.1 + # via + # a2a-sdk + # hatch-fancy-pypi-readme + # opentelemetry-distro + # pythainlp + # uv-dynamic-versioning +pathspec==1.1.1 + # via hatchling +pluggy==1.6.0 + # via hatchling +poetry-core==2.4.1 + # via + # dunamai + # tomlkit +setuptools-scm==10.2.0 + # via + # llama-stack + # llama-stack-api + # pluggy +tomlkit==0.15.0 + # via uv-dynamic-versioning +trove-classifiers==2026.6.1.19 + # via hatchling +uv-dynamic-versioning==0.14.0 + # via a2a-sdk +vcs-versioning==2.2.2 + # via setuptools-scm + +# The following packages are considered to be unsafe in a requirements file: diff --git a/.konflux/requirements.hashes.source.txt b/.konflux/requirements.hashes.source.txt new file mode 100644 index 000000000..041e953d1 --- /dev/null +++ b/.konflux/requirements.hashes.source.txt @@ -0,0 +1,64 @@ +--index-url https://pypi.org/simple +a2a-sdk==0.3.26 \ + --hash=sha256:44068e2d037afbb07ab899267439e9bc7eaa7ac2af94f1e8b239933c993ad52d \ + --hash=sha256:754e0573f6d33b225c1d8d51f640efa69cbbed7bdfb06ce9c3540ea9f58d4a91 +blobfile==3.2.0 \ + --hash=sha256:78514a9265b9aa7d4607042dc77c5e6461ab27036450ad8e1f6ef9a7f29bf958 \ + --hash=sha256:e5e4095477da9f09e2077f41320c006001b2102a61f07d41ceaaecdf5d9741d8 +llama-stack==0.5.4 \ + --hash=sha256:709eb082c77db40e5ca1ed3e1a9d680ece310ea4f52638e4a771ed13676ba573 \ + --hash=sha256:919df1fd4eb4e873420b43a6fce4b74e7ee762853b4627c73b70b0ee1a94c073 +llama-stack-api==0.5.4 \ + --hash=sha256:40fc7fbf901642831cf2ad83bc25013d7f2538463c1bd5ec1b43985c5d6ed90a \ + --hash=sha256:8fec636ecec90b150507ca767d7a6f042df8b6c77acdaba7b397247f5bde3c70 +llama-stack-client==0.5.4 \ + --hash=sha256:6d910c51f917a9ab27b1ec5579fb9167184b9a3a5223abe5c34da123609caf7e \ + --hash=sha256:89b274f46804f2e705d50716576dc6e4f63d15ae545b4eb2933f5caa490b4d8c +opentelemetry-distro==0.63b1 \ + --hash=sha256:b405b04ad70e430390265eb38e82e067a84ca1f49a21429eaadb930c13330d66 \ + --hash=sha256:f435098abc7953f58226e8bf79e4c90bc6b32e50aa75d6fa074201db8243b577 +pycryptodomex==3.23.0 \ + --hash=sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51 \ + --hash=sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6 \ + --hash=sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe \ + --hash=sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c \ + --hash=sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541 \ + --hash=sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e \ + --hash=sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010 \ + --hash=sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245 \ + --hash=sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587 \ + --hash=sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648 \ + --hash=sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff \ + --hash=sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f \ + --hash=sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9 \ + --hash=sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd \ + --hash=sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462 \ + --hash=sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa \ + --hash=sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003 \ + --hash=sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da \ + --hash=sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886 \ + --hash=sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785 \ + --hash=sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744 \ + --hash=sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d \ + --hash=sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8 \ + --hash=sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328 \ + --hash=sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7 \ + --hash=sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545 \ + --hash=sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708 \ + --hash=sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c \ + --hash=sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9 \ + --hash=sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314 \ + --hash=sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5 \ + --hash=sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66 \ + --hash=sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4 \ + --hash=sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08 \ + --hash=sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006 \ + --hash=sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a \ + --hash=sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5 \ + --hash=sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798 \ + --hash=sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c \ + --hash=sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea \ + --hash=sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea +pythainlp==5.3.4 \ + --hash=sha256:76744e51e27c895630bafd74f53a1f0aa8782cef2f7f02eebd6427fe8ce8d84d \ + --hash=sha256:e66fd76fb5931834fd4e32ed54337ec62350d7654f187850e4dd4f915e9f624f diff --git a/.konflux/requirements.hashes.wheel.pypi.txt b/.konflux/requirements.hashes.wheel.pypi.txt new file mode 100644 index 000000000..c1d470df5 --- /dev/null +++ b/.konflux/requirements.hashes.wheel.pypi.txt @@ -0,0 +1 @@ +--index-url https://pypi.org/simple diff --git a/.konflux/requirements.hashes.wheel.txt b/.konflux/requirements.hashes.wheel.txt new file mode 100644 index 000000000..e649c6ffc --- /dev/null +++ b/.konflux/requirements.hashes.wheel.txt @@ -0,0 +1,370 @@ +--index-url https://packages.redhat.com/api/pypi/public-rhai/rhoai/3.4/cpu-ubi9/simple/ +accelerate==1.14.0 \ + --hash=sha256:500b4b21b5aef600ba95bc139d6e31adb23f80652895999cfe7336a71b3161d9 +aiohappyeyeballs==2.6.2 \ + --hash=sha256:6442a34736731d1009c65599056e8d934d058dd8219f79942aee607e1ec3a8da +aiohttp==3.14.1 \ + --hash=sha256:3acb802c339ae082aca8c0f08ba8901087dfdd1bf415ac2ba233072aa6ef5e61 \ + --hash=sha256:6b2069a24c6b9a627aac56d73f18727846757cb0b51968285c421dc4a9ddc24b +aiosignal==1.4.0 \ + --hash=sha256:0894850b54c871dd0365925589ad5dfa70e673059596187e7097e076ea571b1d +aiosqlite==0.22.1 \ + --hash=sha256:6d1ae83321620ec29e2b033ac875b849be78ceecc68c2a35a8d6df22511bfeaa +annotated-doc==0.0.4 \ + --hash=sha256:7efd1fef196515821249f7376bb0d7ba66dcbab04a506376c4fd3582b94ec653 +annotated-types==0.7.0 \ + --hash=sha256:e4e1144fe78e2f54ab359a790d088a55e95caacf44770be1f9b277e91602d6a6 +anyio==4.13.0 \ + --hash=sha256:2d5b8da6df21a1e87f68df768a75f25cd00792b401d6c16e8235bf64642b8316 +asyncpg==0.31.0 \ + --hash=sha256:0bc8a7144ed45aad01e9aae4d607c268de5ffe24df286593082adcf5f8e2cfdc \ + --hash=sha256:bd27352c2cf38a4717585bb5b8fd1c3bd7a7a7bdc67e76f2ca12928b4562c285 +attrs==26.1.0 \ + --hash=sha256:7dbf34c586f6613ef2729b87c361c53883f5c4be5e18e4e4e12bc9f963c13188 +authlib==1.7.2 \ + --hash=sha256:c888b08e47a67a71c41b4c10faf9cffecc699f226e768017fd48b6623e9ac1db +autoevals==0.2.0 \ + --hash=sha256:425567a3ec5ac0a1cc2b83f06af2edff102b05c477f0561b564ff69288fcea7f +azure-core==1.41.0 \ + --hash=sha256:9de3480f266f6b8ceb6af93f0ffcdebc65f4f996f634e07b02306131102d77ff +azure-identity==1.25.3 \ + --hash=sha256:80bea9a8e0287d69b770d0d840c57ce27a115a544985dab269c20b4f8db6f892 +cachetools==7.1.4 \ + --hash=sha256:0a8f17e64eeba0015fcca95580522f2c88e73d56ebcc217b7c277faf9eb40b36 +certifi==2026.5.20 \ + --hash=sha256:494fd905ded06d68e6d0305da7ea79a2b97c0eecc2ddeeda4c58dc230fc2bdcc +cffi==2.0.0 \ + --hash=sha256:51e641fbaafb9e786ee91c957d68a34d27fcdd1c5a790bc4cc73418dba413872 \ + --hash=sha256:9dca0f661e032048781d6ecff83ffd9f1244eddf44542b664564dac2c4fc8a88 +chardet==7.4.3 \ + --hash=sha256:d3d93fc84f932ed155577ee2f1acdd03a98e5c6580b93c3c96d4106ac254b124 +charset-normalizer==3.4.7 \ + --hash=sha256:fedf0c87b67e3fd4fda37c0c64e840bb7e5df197a5d537253632578961a8ce10 +chevron==0.14.0 \ + --hash=sha256:459a4fabb3d608343c3f80d22e98fffa314ce8efd313f5f08ed720e05bf6edee +click==8.4.1 \ + --hash=sha256:87d40c073274de99dca9f76adc0e2900245554d321b9d738adc6827ff83c38de +cryptography==49.0.0 \ + --hash=sha256:2b75762267d1b536f48638cf7b6d0c78eb39bed22eafe5f3859f7e941fa16299 \ + --hash=sha256:497e9f17bd6428fc8513d48d54cade3efa039af9df81cb70ea9ca19f469ff3e2 +datasets==5.0.0 \ + --hash=sha256:ed4a25ead4679d319da2759345b8858c52ba209266e99370de952275ddfb0570 +dill==0.4.1 \ + --hash=sha256:c0b661182fef68f448210a81dcb55a36c837e18284c5380cf1322a4a5c68b8e8 +distro==1.9.0 \ + --hash=sha256:c16b1c9f9666bcb3c7faf41118ede743f387834ae00b6452a5e3253baa8f89c2 +dnspython==2.8.0 \ + --hash=sha256:a46d9ba7ba633e183d4edc6b06d097e88500c0615d37afa0f2525dbf4d5e0468 +docstring-parser==0.18.0 \ + --hash=sha256:ad8ed234ea2dab23080526bd8eaa457a8b1cbabab7c3348723ec5a35a07a0040 +durationpy==0.10 \ + --hash=sha256:ce86a86c56333ed4a1c71ad2f1a0138e6a91ad4144ee31223ac0a08eed8486f3 +einops==0.8.2 \ + --hash=sha256:e14c8f0f96254e435bb5c38dcc1661f1ea10f60865f7deaabb023e5b6127b539 +email-validator==2.3.0 \ + --hash=sha256:3222e63dda823a8552d632f04b6c8ec7c29cbb75dc7dfd74e379c1b681859a73 +emoji==2.15.0 \ + --hash=sha256:dcff2d3342446c7a5b24dd0eea758f13ce3836e78d5b65daead50b382f9a7b56 +faiss-cpu==1.12.0 \ + --hash=sha256:6c3e3786ab287ee87bb0a65c44272c68b7ea367f9bb5295c678af8c2383f018d \ + --hash=sha256:ba4c8234e891f5f594cfe28bbe4a059089babb141db34e9c544159a0bedaeb7c +fastapi==0.136.3 \ + --hash=sha256:7a4d53f0a6638d7ebb0f89ad738493388974431cd7932c13261e2ada1f785c0a +fastuuid==0.14.0 \ + --hash=sha256:54ebb6ac8db73de3e1c231800831ca6e223e4845c4a754543e7628b26aa2d67b \ + --hash=sha256:d95db3aad14a915aa47dc4dc3feae43c159106927dfc25ce7b5d855bc738d3d0 +filelock==3.29.3 \ + --hash=sha256:86dea87cdced6530f39276fd455821d8cbd003f5bdf74565322da0086db365a8 +fire==0.7.1 \ + --hash=sha256:4d2f0321d3d250a399db480d1ff6a04a61a9c9741fb1c16effdfc9025af2993f +frozenlist==1.8.0 \ + --hash=sha256:0aa8fc161fbfe3dd6730357dff925e39fe4bde07d1694ed5f26235a8cb225ab4 \ + --hash=sha256:38fc5ff1cc2847d7b36cc48253868269dfbaabed631c3a71e2d5bcb26cc5ff03 +fsspec==2026.4.0 \ + --hash=sha256:57e9e37e877e67c0bdeaa7024c6c70ec149b2c540896b180e765d5301d1459be +google-api-core==2.31.0 \ + --hash=sha256:13f657cf23fe3623155cc8102545111d68fd7abbb2c5810c864647fa1b421b8c +google-auth==2.53.0 \ + --hash=sha256:a580fd6784815095e84187a6dec1920835fa3e464ad30071c5396f889f51a21d +google-cloud-aiplatform==1.147.0 \ + --hash=sha256:9abef6f4da626a4d33ce98b885e8edd3779e455677b0523d540d1ee75afa5656 +google-cloud-bigquery==3.41.0 \ + --hash=sha256:53a17ff56dd2051f6ec7835a8cef7b1d2268dc8ec36173c0e084b6d72dc868ff +google-cloud-core==2.6.0 \ + --hash=sha256:eae442f1754cdc3b40f7da48bfc6db9bade72970d07818dcbb8b59513212c357 +google-cloud-resource-manager==1.17.0 \ + --hash=sha256:d1fc1e994a0a92634c3a068e584fcd7795a7a46a95177f258610d345b46ed247 +google-cloud-storage==3.11.0 \ + --hash=sha256:2441145a141fa464bba46d84e4bb87ea862c9f42a5cbe9088aa0407cc5aef21a +google-crc32c==1.8.0 \ + --hash=sha256:edfdfaaf68a74fdd3cb96b6c32d0252d75d1a600f91cdc209de1f83e9efb2326 +google-genai==1.75.0 \ + --hash=sha256:1f1b52ddac8979b681275024df6e28ca1eca7bb2808a919099569962c078f1cd +google-resumable-media==2.10.0 \ + --hash=sha256:ba2052ceba01de9529e3fe62c7713f88613df4932dbf19af1bdc88c1cb2c2a18 +googleapis-common-protos==1.75.0 \ + --hash=sha256:3a11f37f3626d1a64371ad7807408d226730fa44cb0f2994510d733afd49d8aa +greenlet==3.4.0 \ + --hash=sha256:1242e934103f79cc1576c77797de9636c689265b1e1d5eae8e21968a58c324a9 \ + --hash=sha256:7609a1094ed19fe2842b9f7446920ff248c228b8d3c57e2612f2ebcf34e4fdf6 +grpc-google-iam-v1==0.14.4 \ + --hash=sha256:3edc8133c9e585f76a75aafef338c2355c04a35c537b03e067ccd34de6fb4bc8 +grpcio==1.80.0 \ + --hash=sha256:52c678447eb080faf6e8c7cba69badae94edf8314942d53082560c65d2423192 \ + --hash=sha256:9344525580dd86c53ddf1d103a3efa7f7ed137e6dfb4b995e639c8eeb60fc0de +grpcio-status==1.80.0 \ + --hash=sha256:ed1089f52c8b708a5bc4de048d5f5dfab668cd8ca41b21cbf578da5920ea9ba2 +h11==0.16.0 \ + --hash=sha256:1f57db4e6320f74869f695cb33197f9a2e956128932c4e3221ada214bafa383b +hf-xet==1.4.3 \ + --hash=sha256:203368fb35bd900901725ac4dbb8c9ebac571c091e8edb27817440e71108de9c \ + --hash=sha256:5b80ed759289c31d69aea744be8c2f8b7ec1f1cc761b2c38d599e1ee81b8a639 +httpcore==1.0.9 \ + --hash=sha256:73b78c918fb6667168af94c14dcc34e864767cbaad55e25d5cdb4692bedee3e4 +httpx==0.28.1 \ + --hash=sha256:6e201f022d2d08ff4ad5e08aaed0ba369851ecc696a4f8ecc59bf30140aa439e +httpx-sse==0.4.3 \ + --hash=sha256:81bf5b6e5b4a82d6e8d3dec046b90a63eaa4185b0ec3e78c56503f609d7b41d5 +huggingface-hub==1.13.0 \ + --hash=sha256:a688bb15a69610080d2c6222c148aab69583b7165ad4b9ec8ead01631dbeb103 +idna==3.18 \ + --hash=sha256:c8b8ebcb022a6b5c7925661877d5a5b2d3fffa4747ff09d5355833ac38436f81 +importlib-metadata==8.9.0 \ + --hash=sha256:c171bb9848f86dc92ab7d1113876236dde3d12de64356db70fe78fd5daeb7fe6 +jinja2==3.1.6 \ + --hash=sha256:61b4b68331c4f1900566fe55e09f6a7ca849ce0a90cd070935b4f6ecefa3b86e +jiter==0.14.0 \ + --hash=sha256:84c1562c21fffbc2484ae9981fcc6831024498f1ebd444172683ff84bc2fff66 \ + --hash=sha256:9d40519aa849502cb4490a550a63b756ca053074fd48c8df4a36ed7650e8eb10 +joblib==1.5.3 \ + --hash=sha256:8b029622b134d6d00b1f1f57921a9bbd4da89eeb2410c64c996794ffe656534f +joserfc==1.7.1 \ + --hash=sha256:0c2c1148d6372221d9e9532683797231804932a7e36f873b207f690c3647e3a8 +jsonpath-ng==1.8.0 \ + --hash=sha256:5090a8cd15a3afeb65fdd5393e8f1a59e4972bfc06da3f6abe830760516027ad +jsonschema==4.26.0 \ + --hash=sha256:6d92fe1a0ae3d61ce1ccf3af5d3616464d7200b39aa8b624c31fe492bff39726 +jsonschema-specifications==2025.9.1 \ + --hash=sha256:10ed21850aa9c5556850aee3cfbb51d51ec74b223b67f893d6a26c960da02ef5 +kubernetes==36.0.2 \ + --hash=sha256:8aa7020e9d21f10413c9af009a23620f08565db0faf459d844731b5660a1d250 +langdetect==1.0.9 \ + --hash=sha256:196fd54e18d9baf5c10151ece5f63c662059c61e245b687fc5e65c52ae28b32b +litellm==1.84.0 \ + --hash=sha256:0cd97e03255433ec1d9c9e904be7feba14cf1ae94c3a28af3f1fda83c5cafe00 +lxml==6.1.1 \ + --hash=sha256:7204c8b515a2f5bd5dca8119bc98666cc7f47cdee48c231d3f9bab0e8cd13c12 \ + --hash=sha256:95731fd714a28ae207066c3350379dd4c33cf68f55284e332b1ae65cccf2848f +markdown-it-py==4.2.0 \ + --hash=sha256:3e627612fe8f84f223658572974ece386ace011cf06f07c244918a70ac028139 +markupsafe==3.0.3 \ + --hash=sha256:10c0e3022c2cead724cb6e31b1cc368b423b2995a9461d3ab844f1eb894b6ed1 \ + --hash=sha256:3e5e2eda8f7612f1e59c261040e824f4a7a80872df58731ca46f248646d6356b +mcp==1.27.2 \ + --hash=sha256:86a49d49de6ca19b0205950783df36633441eb8930aae707ced24ac786f8e7f7 +mdurl==0.1.2 \ + --hash=sha256:6d8be534d152bbbbd9e3866e8addbacae1fb09801f0436469909c7295ae89f6e +mpmath==1.3.0 \ + --hash=sha256:3f99b96490a257d46c5213e4477034871c604571ed7bee5904aea43983838862 +msal==1.37.0 \ + --hash=sha256:0b1b3032a137904a7bc6696c4b3389cf76892bdc4a38002ef9e02f5a86105482 +msal-extensions==1.3.1 \ + --hash=sha256:d4a6b6115008b68ad4fdf846a90ff7f2715beb7bfddfc5d7345bc16e1c8daaef +multidict==6.7.1 \ + --hash=sha256:10baf21a3391c03a4064c51e1bd4929de589f86f3808744f4aeda5a88f39a345 \ + --hash=sha256:913435ce2a574e3a9d9b360051667fd45dcc9bf618daa700a5782bf4209b31f8 +multiprocess==0.70.16 \ + --hash=sha256:9e6258d8ce50f14643ec82b24ffa9956ac93e2ae79560582a707b0d1844b8666 +networkx==3.6.1 \ + --hash=sha256:d76ca8a5294909e27b386d2150b4f6da6b5bd6fdc8142f7161949522b3eee35b +nltk==3.9.4 \ + --hash=sha256:27395989b730b05691b55acfd1dbf4020400c8fbff6be792b5aafaed3f847c83 +numpy==2.3.5 \ + --hash=sha256:076f7d4be8aee972794b040029e028267d2743781b51295c16ba94df2896e9e7 \ + --hash=sha256:6b8b95d9db3c0348dc3de505ffe7af316580cc2bc468cf0263f7b2ad96c81d34 +oauthlib==3.3.1 \ + --hash=sha256:1bf73c8e0aead3761794f6e5b15228bee706cda020facd0df347c145843adf40 +openai==2.41.1 \ + --hash=sha256:ebaf9e2016e193ca5b417619f2b860dd31453e239667711b4f916fdb1c75e04e +opentelemetry-api==1.42.1 \ + --hash=sha256:11eafcf5dae7858122d69da01695f0989e2c71dd525e24427ced799b8860ef60 +opentelemetry-exporter-otlp==1.42.1 \ + --hash=sha256:d2cf9646a6cd44d7cddea236caed67273e201f0d300aa363f77ae2049f1bf842 +opentelemetry-exporter-otlp-proto-common==1.42.1 \ + --hash=sha256:e2dacf018525703ed19630caac157ef31271358e59dac9d9fd063154aff24b70 +opentelemetry-exporter-otlp-proto-grpc==1.42.1 \ + --hash=sha256:231914f610c1c4a7ba988036c663d20afdfe458a3ac0267ae94c970094668def +opentelemetry-exporter-otlp-proto-http==1.42.1 \ + --hash=sha256:5566eb7667c67ce35de55920675f432ac51ce4d2f75ffd7a3ff7903c72695157 +opentelemetry-instrumentation==0.63b1 \ + --hash=sha256:4137ed2e1c8e140fb351abedccd0f3bf19bd3998f9c68cb727aaa51674bb45b5 +opentelemetry-proto==1.42.1 \ + --hash=sha256:429902838bababd11e1c975d743ea3313cb03a9000c4bf124d80a3ba7456fd26 +opentelemetry-sdk==1.42.1 \ + --hash=sha256:dd9749618efe3d956229734b22f893f61362e9c63f2565dc7753ca473889e219 +opentelemetry-semantic-conventions==0.63b1 \ + --hash=sha256:894cbc42a11f9653f101b7b008d56c67ce3f888563e6b4ae39dc9288ca31536b +packaging==25.0 \ + --hash=sha256:ea1dc775c4d009b9af3848d90c8a5252ad652f3ae251439318eb0bb09c3c682f +pandas==3.0.2 \ + --hash=sha256:615bf8c0b5ea16faef2ea2827ec80892232c746cbe2e0d1bbee84c3f68254055 \ + --hash=sha256:adf5d48f0407c277ebf624b35ae45313b4f6def71cb27a786b84c6189dcb93b3 +peft==0.19.1 \ + --hash=sha256:d0b98d5a5a456c0445495fca64be52f985e0dfff57c4f72020d92c0dc3eb9f08 +pillow==12.2.0 \ + --hash=sha256:1d14bc0f26c4f17eae2bddfe2700bcc922c5e19bebad32145a52445ad0b2909d \ + --hash=sha256:394981fc7445b3721bc5dd88b5859050eedfa51e307a986fdf832d2145555f59 +polyleven==0.11.0 \ + --hash=sha256:952335347f97afe6477d325af4ba62d01806ec728f69bc22dee6908466d4cea3 \ + --hash=sha256:fac5592f992760680817f73c7b18b1a82151d37410584454aa162d2010b587fb +prometheus-client==0.25.0 \ + --hash=sha256:ad5ecb750327dd1002239ec35222a384db45753c87242d48e96f219db05d9591 +prompt-toolkit==3.0.52 \ + --hash=sha256:2df5dea7ebf2cf3b054ac3f14fa76ab44dda5d9deeb3911a1a27416fc0864c1c +propcache==0.4.1 \ + --hash=sha256:2c44e59f67229ce44cd71d778fc1c3c2ada5d98f0e3a383eb65129bbddab6755 \ + --hash=sha256:c926067c28febdef60d1d3eb7992c9ab7a9e14631411492e335a9f6c08c7379a +proto-plus==1.28.0 \ + --hash=sha256:8d5189479a0eb3a1215aa9821c72bf4419b6d430718035d8dc548cbc85541288 +protobuf==6.33.6 \ + --hash=sha256:c6e1ea700f5e031c9669bd535a87f694f09b74e8b83885ab2759ae2bd2cbbd94 \ + --hash=sha256:df80a6563854e01cd6672f462b18d7ffff7f23caca920a972a93ba14cd20af1e +psutil==7.2.2 \ + --hash=sha256:ead730f5194d50ca6ddcfb93d4e771a74b71ca5217c7fafc3f9264735416eb93 \ + --hash=sha256:fa7af6bad048d2c4f8af0f0ab2ed2857ff650a963e90bd3cacb394962ccb5803 +psycopg2-binary==2.9.11 \ + --hash=sha256:0075b19b23dfbe894fd10b060332e157544b333e23416bdc59bea4b634577add \ + --hash=sha256:e87099ce8aa6f5a858258add8eb9d4e971e80d44660a9b06cc0d4e4bc2283512 +pyaml==26.2.1 \ + --hash=sha256:faf3687c32c7820003af8562442d1e473fe0a713025162efb8331dc0dfc9d29c +pyarrow==23.0.1 \ + --hash=sha256:21855f7fa76d32250847d3f73aefbc6993e6e92b7c48a2651aeedbdc1a25e37a \ + --hash=sha256:317d00a32e92228ad0491105e6e153f09b4f190e1eabc652c17fbbc104ea6324 +pyasn1==0.6.3 \ + --hash=sha256:51194c7b4281edb039247c23e11f5280b4c9713ec246fda1fa9b5d960b860e51 +pyasn1-modules==0.4.2 \ + --hash=sha256:789033a94c5074562252314a4362858e2c173b04c7ae8dc1173ac69ce1f5cd7e +pycparser==3.0 \ + --hash=sha256:abc09eabddd4d1d5d1606f6e113c0176507c75805c97e0e2af2c99c76eefbb9d +pydantic==2.13.1 \ + --hash=sha256:6eab392063f50f266f91bd61d6946ef3e0181c65240f46b91d527b1246097394 +pydantic-core==2.46.1 \ + --hash=sha256:1c2f8b60e6b8e3443816fb14260499707757e8ab68af11b0a4ebe67ce1839ce2 \ + --hash=sha256:f7a3fa79699060131a7b63c29711198953ed1f96a78b7ee7da9d5ab9e5276863 +pydantic-settings==2.14.2 \ + --hash=sha256:794c2942e9f6c7f0a3482aebd328cf08213f4770fdc560ac5d5084a0fce81e7f +pygments==2.20.0 \ + --hash=sha256:22eb8865e5accd0fc529e4fdc5cf74db3e6b2b6348b43a089950c8ce671dedb3 +pyjwt==2.13.0 \ + --hash=sha256:43798d73ce4c0a7221087b220935ade361955eeada0b3b4f8622f9b53916ea1c +python-dateutil==2.9.0.post0 \ + --hash=sha256:89e5d6f444e51ac0a18370406b265fa1f51c88824476fd08c56db669dd317775 +python-dotenv==1.2.2 \ + --hash=sha256:58bee80c6e48ce9d2b6bcb805bf670d3b45438911bb32e82f9145369b9deaf63 +python-multipart==0.0.32 \ + --hash=sha256:440771a83ee1170ce817dd5a8c4c4a834dbfb92a904dea257e737360259c76c3 +pyyaml==6.0.3 \ + --hash=sha256:e647b656314603fe964279e192001153683e6a5574a3011f6c4efa6f821a416c \ + --hash=sha256:fc30b3ff55268f3ae334b94073d189f32cded01ccbcd177b7537dff511fbe54b +referencing==0.37.0 \ + --hash=sha256:2848a91042246ed5cb2da615736b66252fb95d7b5385e9d1f889ea2b8ba01e3d +regex==2026.4.4 \ + --hash=sha256:11710cc1f0e40d5e4e2dc019df2be9564bd684027a72cca733f13c28ae2193e5 \ + --hash=sha256:cbaa507ca7cdd187a4e0080296799e98e317e975cd099612f812f532ccb86609 +requests==2.34.2 \ + --hash=sha256:c3028149759c7fa710f2814e499608083a26d88db09369dd4063054373d77487 +requests-oauthlib==2.0.0 \ + --hash=sha256:6fc02f497fb599e42b5811806e49498d26ebb58b6195a37962c8ea1557b44205 +rich==15.0.0 \ + --hash=sha256:b61783d48185f1880510afc6a3774ffbc32a5b9b31cd62dd5c5ff71b9feeea74 +rpds-py==0.30.0 \ + --hash=sha256:11729e4f36a649a22271d688f0edf12b4d5a3ea0c6f6d5c2a0adaa659be540a8 \ + --hash=sha256:3cd14f655cf15503309f9e3c021571429331f26686278daeee9793f01187fd43 +safetensors==0.7.0 \ + --hash=sha256:8c460d137ae97b008304970788bc8c627a920a38a1cf4da7a4592caab834e86d \ + --hash=sha256:a2d4e90fbba3b892e81d77a8add3404d9a2111cc084d89805e296ce535f57363 +scikit-learn==1.8.0 \ + --hash=sha256:243145adfc8a91d1a48490be09d330f79a3b716ecbc06daa118ee92aa81b7af6 \ + --hash=sha256:3f3e234d9b09333b255edfc42457b089900cbb8e0defaecead9021a4de056d59 +scipy==1.17.1 \ + --hash=sha256:a2a217eeb0c6f690e01fec8652370ba23ebd9315804d6ebdfedfc65081a4514e \ + --hash=sha256:db634975201f3747d80d4fef5d7954a1f64b72485eeb1dfe1ce2d9f3d3eebdee +semver==3.0.4 \ + --hash=sha256:d493aa9269842671b402c5325f37529f4b86819f18fc4b861e7ebefedb4937ed +sentence-transformers==5.2.0 \ + --hash=sha256:fe42b1dde3a978e94188e1826353b18530770ffad06e516526d7c6b2937926db +setuptools==80.10.2 \ + --hash=sha256:05ef2ee3d34409715c7d0589a3a0c6064a2b117f8489a5b512aef078173d1faf +shellingham==1.5.4 \ + --hash=sha256:1d8fcda93b78926bb91867c60ef1dd91eaccb73695420bdab7c220e2360ca2c6 +six==1.17.0 \ + --hash=sha256:be25bf700236019335048807404f5f0a00b67b20ce1091ccb7bbd42b7beded23 +sniffio==1.3.1 \ + --hash=sha256:10c13dae10df70efde4ea062208bebf03d9a36cc0b6cf4f8756cbb5c2cac0a05 +sqlalchemy==2.0.49 \ + --hash=sha256:2ae54c222fe9b0076e6c33ad0fd102b73001618408045f56ea6ca9ec4eb3709b \ + --hash=sha256:77bbdc3a1021c44465c1f67326c66af6a07c1ed791b29aa42a1fd23e775e6ec2 +sse-starlette==3.4.4 \ + --hash=sha256:d708ce9dbef3736fb9cf2f0903906848a7521073c86828c62d2fda04ae8c05f6 +starlette==1.3.1 \ + --hash=sha256:1076ce1d57b000b509f126da9bb439b4af3ebd798fe4fa998a02640d4d3fe92f +sympy==1.14.0 \ + --hash=sha256:925c516e987e5ca517a490e7aa022d6e11ddb5f2c208f8aedcd3a4be9db7b389 +tenacity==9.1.4 \ + --hash=sha256:d7a56fc6d3929e363f1771dfb795642c2358d05facbce0a088296e867aecf1e1 +termcolor==3.3.0 \ + --hash=sha256:d53f88e05676950850fc95fd0a86c97b672f2ea88aa55928b91146d8cdbdfdce +threadpoolctl==3.6.0 \ + --hash=sha256:b65c2b90252c3ac0074bab96dc50655115fc10e3b659b0c4e708cf59226ce1d0 +tiktoken==0.12.0 \ + --hash=sha256:57d67fb66a1f0f679b1d85059f9297777fb5076bfdab1d4bd69e2536d8db035d \ + --hash=sha256:da579c5b4059712fa0bf08a3b31858720407220ed406fc0253be147c3ab741d6 +tokenizers==0.22.2 \ + --hash=sha256:1d65c6f9704bc7a0764b5b5b6ec010a4a54a5afa15d7a6573ba564eb77b01c56 \ + --hash=sha256:b9f7cca0fb1d65e2c714c7d296cd6a0e82719826af31a67fdaef33289bab3d27 +torch==2.10.0 \ + --hash=sha256:72245dc7da092de256960e744aeafa31ce46c7faa4d2f533bf8439b19b39709b \ + --hash=sha256:e4c5b250ac3cf86fe3e18327b210981d3a6dacb009ea2cdbbc529f56fbc41f4a +tornado==6.5.7 \ + --hash=sha256:145f60cf4a02ca464e093d4ec650c383566ec2a9c72a4baa823a2993fe23efe6 \ + --hash=sha256:682d70fa5baa06d10b90512b066587c785f387bfe0bd9c48d31b39e2bc1d40eb +tqdm==4.68.2 \ + --hash=sha256:2e65c08da81848d2df6dc5c0cae5e53bd64c4e4bd5a34962ca835c530be82485 +transformers==5.12.0 \ + --hash=sha256:6a6074d7d19508b248711fcebb5d238ab74fd87682320326117c2ebc936d4d6e +tree-sitter==0.25.2 \ + --hash=sha256:2349e7dc6b15f5a459c87261511210888e4de5028438ea20e96a84e700b3d8ae \ + --hash=sha256:cf36dce9178cb12706afca3c52260faa1a52a25e8528e3b88c80989d6746d7e9 +triton==3.6.0 \ + --hash=sha256:be9c508fd750b5c80fc047fae693f17e0a77f45389825ec1479933973f2c3dec \ + --hash=sha256:d75ba84559baf501d2c82534cdfb62131805df8e5d58010730f059b3f8bb9b93 +trl==1.6.0 \ + --hash=sha256:7fab84f7ca1c811680ceb77d76998979340d2a92f52236c8eadf1c80d1ecd247 +typer==0.26.7 \ + --hash=sha256:3d2d387ebed3c751d31cdd86767bbfcc1a117b94bb177885ce42804d5a87a522 +typing-extensions==4.15.0 \ + --hash=sha256:429f20623a355887563afc68041e782f059f26031a5e00e2dd302d37799f672c +typing-inspection==0.4.2 \ + --hash=sha256:14def1455626f831da791382c9c46592418e10462b296b3521ec30c1d28b4d75 +urllib3==2.7.0 \ + --hash=sha256:fa362060799d5cec112613712db5e3abd6c59c962cf2f532a3bcbb19f52c4750 +uvicorn==0.49.0 \ + --hash=sha256:0d4a19be044c20a4f074656fac90fcdc7759b0fcb2b70df46d061d94235f9be1 +wcwidth==0.8.1 \ + --hash=sha256:80296cda7f1bc552fc39c255d918b1c36f0d1d33010232c99ce60a29dc1b02d3 +websocket-client==1.9.0 \ + --hash=sha256:c1370b3fea1c97189614828173ec02a8feaab0004782381f03456e1db4c60f19 +websockets==16.0 \ + --hash=sha256:6d14ef228bb305deeec8e0769304d30ac1f69a4e8b29ca17d6968bbeecf1901f \ + --hash=sha256:72a0963c315c53521f807af0ca4f1d84c0ca378e4125e13c6694120781aef60b +wrapt==2.1.2 \ + --hash=sha256:4ec7d1134beba9bd8d220ad31d52ffb98fe26613584bd71b1237a8ecfec78d1b \ + --hash=sha256:5d2b5e2f50bbc64e581d04adb797f24be06d91d2ce872fd6a7ff63d07c46f3cc +xxhash==3.6.0 \ + --hash=sha256:c03a08fd89c01fc60763cfe92f93a4b14ec05e110577c2e41640942e504aee38 \ + --hash=sha256:c7948e01ae9e33b691c9531409ecaf7c36f143764ab10171c7c0833c58588a3f +yarl==1.23.0 \ + --hash=sha256:255be9da2dc3317c3333938906d73e55ff830b9b4b4a450a4bcfebbcff872434 \ + --hash=sha256:4cd7f2925038c86bb461a722ce74e964e98bfe80b3a0c3bce3341f549fb3eb6b +zipp==4.1.0 \ + --hash=sha256:8350a43d076f3ce36df498c18cf456f8e1bc81d07cc9b70c56029946fbe0b239 diff --git a/.konflux/requirements.hermetic.txt b/.konflux/requirements.hermetic.txt new file mode 100644 index 000000000..e69de29bb diff --git a/.konflux/requirements.overrides.txt b/.konflux/requirements.overrides.txt new file mode 100644 index 000000000..4d404920f --- /dev/null +++ b/.konflux/requirements.overrides.txt @@ -0,0 +1,3 @@ +# override these package to the version available on RHOAI wheels index: +# https://packages.redhat.com/api/pypi/public-rhai/rhoai/3.4/cpu-ubi9/simple +packaging<26 diff --git a/rpms.in.yaml b/.konflux/rpms.in.yaml similarity index 66% rename from rpms.in.yaml rename to .konflux/rpms.in.yaml index ca64f8cf2..ac6928fce 100644 --- a/rpms.in.yaml +++ b/.konflux/rpms.in.yaml @@ -7,6 +7,12 @@ packages: cmake, cargo, ] +upgradePackages: + [ + gnutls, + libxslt, + openssl-fips-provider, + ] contentOrigin: repofiles: ["./redhat.repo"] arches: [x86_64, aarch64] diff --git a/rpms.lock.yaml b/.konflux/rpms.lock.yaml similarity index 72% rename from rpms.lock.yaml rename to .konflux/rpms.lock.yaml index 2ca7ed624..ab7e9adcb 100644 --- a/rpms.lock.yaml +++ b/.konflux/rpms.lock.yaml @@ -32,20 +32,6 @@ arches: name: cmake-rpm-macros evr: 3.26.5-2.el9 sourcerpm: cmake-3.26.5-2.el9.src.rpm - - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/appstream/os/Packages/g/gcc-c++-11.5.0-5.el9_5.aarch64.rpm - repoid: rhel-9-for-aarch64-appstream-eus-rpms - size: 12999288 - checksum: sha256:a9ff0bd2a2b3483e07dcf87f8137a6358f36f5300c934b90500f119f884e3463 - name: gcc-c++ - evr: 11.5.0-5.el9_5 - sourcerpm: gcc-11.5.0-5.el9_5.src.rpm - - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/appstream/os/Packages/l/libstdc++-devel-11.5.0-5.el9_5.aarch64.rpm - repoid: rhel-9-for-aarch64-appstream-eus-rpms - size: 2526795 - checksum: sha256:83a2006137335a9b17a05a02a54481abcdfd295b280b924c51caaacd7bf07ad6 - name: libstdc++-devel - evr: 11.5.0-5.el9_5 - sourcerpm: gcc-11.5.0-5.el9_5.src.rpm - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/appstream/os/Packages/l/libuv-1.42.0-2.el9_4.aarch64.rpm repoid: rhel-9-for-aarch64-appstream-eus-rpms size: 150129 @@ -53,6 +39,13 @@ arches: name: libuv evr: 1:1.42.0-2.el9_4 sourcerpm: libuv-1.42.0-2.el9_4.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/appstream/os/Packages/l/libxslt-1.1.34-13.el9_6.2.aarch64.rpm + repoid: rhel-9-for-aarch64-appstream-eus-rpms + size: 244531 + checksum: sha256:b87f5b4991bb04771ce1c6c26611a9eeb754fa80ffc257199e68db6ed5f03959 + name: libxslt + evr: 1.1.34-13.el9_6.2 + sourcerpm: libxslt-1.1.34-13.el9_6.2.src.rpm - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/appstream/os/Packages/p/patch-2.7.6-16.el9.aarch64.rpm repoid: rhel-9-for-aarch64-appstream-eus-rpms size: 129037 @@ -81,6 +74,13 @@ arches: name: ed evr: 1.14.2-12.el9 sourcerpm: ed-1.14.2-12.el9.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/baseos/os/Packages/g/gnutls-3.8.3-6.el9_6.4.aarch64.rpm + repoid: rhel-9-for-aarch64-baseos-eus-rpms + size: 1051990 + checksum: sha256:e766b1d568983ae2dff7ae7ccec729f5edd49795ef367a806ed06810ee4c68de + name: gnutls + evr: 3.8.3-6.el9_6.4 + sourcerpm: gnutls-3.8.3-6.el9_6.4.src.rpm - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/baseos/os/Packages/i/info-6.7-15.el9.aarch64.rpm repoid: rhel-9-for-aarch64-baseos-eus-rpms size: 230301 @@ -88,6 +88,20 @@ arches: name: info evr: 6.7-15.el9 sourcerpm: texinfo-6.7-15.el9.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/baseos/os/Packages/o/openssl-fips-provider-3.0.7-11.el9_0.aarch64.rpm + repoid: rhel-9-for-aarch64-baseos-eus-rpms + size: 10281 + checksum: sha256:29f0ed0694669438654fd2f7ed288262aafddd84f09ff9fa017cc71ee4990df1 + name: openssl-fips-provider + evr: 3.0.7-11.el9_0 + sourcerpm: openssl-fips-provider-3.0.7-11.el9_0.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/aarch64/baseos/os/Packages/o/openssl-fips-provider-so-3.0.7-11.el9_0.aarch64.rpm + repoid: rhel-9-for-aarch64-baseos-eus-rpms + size: 522978 + checksum: sha256:6699bd711fa3e30f576a062029c62529f362a93657066dd87fc8704d730cff42 + name: openssl-fips-provider-so + evr: 3.0.7-11.el9_0 + sourcerpm: openssl-fips-provider-3.0.7-11.el9_0.src.rpm source: [] module_metadata: [] - arch: x86_64 @@ -120,20 +134,6 @@ arches: name: cmake-rpm-macros evr: 3.26.5-2.el9 sourcerpm: cmake-3.26.5-2.el9.src.rpm - - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/appstream/os/Packages/g/gcc-c++-11.5.0-5.el9_5.x86_64.rpm - repoid: rhel-9-for-x86_64-appstream-eus-rpms - size: 13479598 - checksum: sha256:b8392274e302d665bc132aee4ed023f8a777d9c446531679ede18150d7867189 - name: gcc-c++ - evr: 11.5.0-5.el9_5 - sourcerpm: gcc-11.5.0-5.el9_5.src.rpm - - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/appstream/os/Packages/l/libstdc++-devel-11.5.0-5.el9_5.x86_64.rpm - repoid: rhel-9-for-x86_64-appstream-eus-rpms - size: 2531717 - checksum: sha256:84695eeeb1daa8ff74baf7efd9fc57fb136bec7e8a2ca56c105be6d83ec22d07 - name: libstdc++-devel - evr: 11.5.0-5.el9_5 - sourcerpm: gcc-11.5.0-5.el9_5.src.rpm - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/appstream/os/Packages/l/libuv-1.42.0-2.el9_4.x86_64.rpm repoid: rhel-9-for-x86_64-appstream-eus-rpms size: 154427 @@ -141,6 +141,13 @@ arches: name: libuv evr: 1:1.42.0-2.el9_4 sourcerpm: libuv-1.42.0-2.el9_4.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/appstream/os/Packages/l/libxslt-1.1.34-13.el9_6.2.x86_64.rpm + repoid: rhel-9-for-x86_64-appstream-eus-rpms + size: 247315 + checksum: sha256:fbaad5f1ca8422cd20ede7324bf4fc2cc013e5ef106f698f89f50271727b6152 + name: libxslt + evr: 1.1.34-13.el9_6.2 + sourcerpm: libxslt-1.1.34-13.el9_6.2.src.rpm - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/appstream/os/Packages/p/patch-2.7.6-16.el9.x86_64.rpm repoid: rhel-9-for-x86_64-appstream-eus-rpms size: 133240 @@ -169,6 +176,13 @@ arches: name: ed evr: 1.14.2-12.el9 sourcerpm: ed-1.14.2-12.el9.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/baseos/os/Packages/g/gnutls-3.8.3-6.el9_6.4.x86_64.rpm + repoid: rhel-9-for-x86_64-baseos-eus-rpms + size: 1127930 + checksum: sha256:4e2c0319c285bf9be7db3f2ec0e1437c07f99ce2f7b32f3de4de946670119dfc + name: gnutls + evr: 3.8.3-6.el9_6.4 + sourcerpm: gnutls-3.8.3-6.el9_6.4.src.rpm - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/baseos/os/Packages/i/info-6.7-15.el9.x86_64.rpm repoid: rhel-9-for-x86_64-baseos-eus-rpms size: 233806 @@ -176,5 +190,19 @@ arches: name: info evr: 6.7-15.el9 sourcerpm: texinfo-6.7-15.el9.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/baseos/os/Packages/o/openssl-fips-provider-3.0.7-11.el9_0.x86_64.rpm + repoid: rhel-9-for-x86_64-baseos-eus-rpms + size: 10316 + checksum: sha256:9cd99e98e91da4b15eb773b4be035aaee63a369fda70cbb38371455c3217bed1 + name: openssl-fips-provider + evr: 3.0.7-11.el9_0 + sourcerpm: openssl-fips-provider-3.0.7-11.el9_0.src.rpm + - url: https://cdn.redhat.com/content/eus/rhel9/9.6/x86_64/baseos/os/Packages/o/openssl-fips-provider-so-3.0.7-11.el9_0.x86_64.rpm + repoid: rhel-9-for-x86_64-baseos-eus-rpms + size: 588706 + checksum: sha256:8b6af65ac0eafbd1eb85d0f73c8255ea979418032fd4aa189a4a65004bcf1ef3 + name: openssl-fips-provider-so + evr: 3.0.7-11.el9_0 + sourcerpm: openssl-fips-provider-3.0.7-11.el9_0.src.rpm source: [] module_metadata: [] diff --git a/.tekton/lightspeed-stack-0-5-pull-request.yaml b/.tekton/lightspeed-stack-0-5-pull-request.yaml new file mode 100644 index 000000000..9941faee6 --- /dev/null +++ b/.tekton/lightspeed-stack-0-5-pull-request.yaml @@ -0,0 +1,663 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/lightspeed-core/lightspeed-stack?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/pull_request_number: '{{pull_request_number}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/cancel-in-progress: "true" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "pull_request" && target_branch + == "release/0.5" + labels: + appstudio.openshift.io/application: lightspeed-core-0-5 + appstudio.openshift.io/component: lightspeed-stack-0-5 + pipelines.appstudio.openshift.io/type: build + name: lightspeed-stack-0-5-on-pull-request + namespace: lightspeed-core-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/redhat-user-workloads/lightspeed-core-tenant/lightspeed-stack-0-5:on-pr-{{revision}} + - name: image-expires-after + value: 5d + - name: build-platforms + value: + - linux/x86_64 + - linux-c6gd2xlarge/arm64 + - name: build-source-image + value: 'true' + - name: prefetch-input + value: | + [ + { + "type": "rpm", + "path": ".konflux" + }, + { + "type": "generic", + "path": "." + }, + { + "type": "pip", + "path": ".konflux", + "requirements_files": [ + "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", + "requirements.hashes.source.txt", + "requirements.hermetic.txt" + ], + "requirements_build_files": ["requirements-build.txt"], + "binary": { + "packages": "accelerate,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,cachetools,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,faiss-cpu,fastapi,fastuuid,filelock,fire,frozenlist,fsspec,google-api-core,google-auth,google-cloud-aiplatform,google-cloud-bigquery,google-cloud-core,google-cloud-resource-manager,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,huggingface-hub,idna,importlib-metadata,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,kubernetes,langdetect,litellm,lxml,markdown-it-py,markupsafe,maturin,mcp,mdurl,mpmath,msal,msal-extensions,multidict,multiprocess,networkx,nltk,numpy,oauthlib,openai,opentelemetry-api,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,packaging,pandas,peft,pillow,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,python-dateutil,python-dotenv,python-multipart,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,semver,sentence-transformers,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,typer,typing-extensions,typing-inspection,urllib3,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp", + "os": "linux", + "arch": "x86_64,aarch64", + "py_version": 312 + } + } + ] + - name: hermetic + value: 'true' + - name: dockerfile + value: Containerfile + - name: build-args-file + value: .konflux/build-args-konflux.conf + pipelineSpec: + description: | + This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. + + _Uses `buildah` to create a multi-platform container image leveraging [trusted artifacts](https://konflux-ci.dev/architecture/ADR/0036-trusted-artifacts.html). It also optionally creates a source image and runs some build-time tests. This pipeline requires that the [multi platform controller](https://github.com/konflux-ci/multi-platform-controller) is deployed and configured on your Konflux instance. Information is shared between tasks using OCI artifacts instead of PVCs. EC will pass the [`trusted_task.trusted`](https://conforma.dev/docs/policy/packages/release_trusted_task.html#trusted_task__trusted) policy as long as all data used to build the artifact is generated from trusted tasks. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build-multi-platform-oci-ta?tab=tags)_ + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter path-context + name: dockerfile + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + type: string + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "true" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: docker + description: The format for the resulting image's mediaType. Valid values are oci or docker. + name: buildah-format + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: "false" + description: Whether to enable privileged mode, should be used only with remote VMs + name: privileged-nested + type: string + - default: + - linux/x86_64 + description: List of platforms to build the container images on. The available set of values is determined by the configuration of the multi-platform-controller. + name: build-platforms + type: array + - name: enable-cache-proxy + default: 'false' + description: Enable cache proxy configuration + type: string + - name: enable-package-registry-proxy + default: 'true' + description: Use the package registry proxy when prefetching dependencies + type: string + - name: sast-target-dirs + type: string + default: . + description: Target directories to scan with SAST tools. Multiple values should be separated with commas. + results: + - description: "" + name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - description: "" + name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - description: "" + name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - description: "" + name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: enable-cache-proxy + value: $(params.enable-cache-proxy) + taskRef: + params: + - name: name + value: init + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:5a423246792ac501ea279229b42ee57da9927da441c04b5c9ff86817b0856b08 + - name: kind + value: task + resolver: bundles + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + params: + - name: name + value: git-clone-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:d30f13dd15daf89dd6dc645243b3444d35570d13f7840c3fd65e366022515205 + - name: kind + value: task + resolver: bundles + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + - name: enable-package-registry-proxy + value: $(params.enable-package-registry-proxy) + runAfter: + - clone-repository + taskRef: + params: + - name: name + value: prefetch-dependencies-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:3dc78afbf3a441e0280067433cb28ea3d2d0088ec214c73bf063f145b4f273ef + - name: kind + value: task + resolver: bundles + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - matrix: + params: + - name: PLATFORM + value: + - $(params.build-platforms) + name: build-images + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: PRIVILEGED_NESTED + value: $(params.privileged-nested) + - name: SOURCE_URL + value: $(tasks.clone-repository.results.url) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: IMAGE_APPEND_PLATFORM + value: "true" + - name: HTTP_PROXY + value: $(tasks.init.results.http-proxy) + - name: NO_PROXY + value: $(tasks.init.results.no-proxy) + runAfter: + - prefetch-dependencies + taskRef: + params: + - name: name + value: buildah-remote-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:77007259cc87f32d63d2c201226aadaab98313cfd4e02b46abc243c4d2cc27bd + - name: kind + value: task + resolver: bundles + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-images.results.IMAGE_REF[*]) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + runAfter: + - build-images + taskRef: + params: + - name: name + value: build-image-index + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.3@sha256:b33bfa8dc27dbf459f0779598ba45dcaa490bcc9f8efe1652bcf360ec8cb5582 + - name: kind + value: task + resolver: bundles + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: BINARY_IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: source-build-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:8567bb7bf8fa9147c96b297533336fa7079ecf972cb86c09ccdd6bddedb25711 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.build-source-image) + operator: in + values: + - "true" + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: deprecated-image-check + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:e78d0d3baf3c8cfc1a5ad278196b74032d9568b143a87c7a79ab780fedfb296e + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-platform + value: + - $(params.build-platforms) + name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clair-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:8fad4c2e2f470f82ee43d6b2ac72327b4d9c6e9cb514a678911c1c9359c29894 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: platform + value: + - $(params.build-platforms) + name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: ecosystem-cert-preflight-checks + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:88f4fd6d7812a3c46f120f3035974f5fb8cb06b5e3e927badf6e8370f1516a88 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: ARGS + value: --project-name=lightspeed-stack --report --org=dca2ca89-7e51-4a3a-b7a5-6ad5633057b8 + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-snyk-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:0ebf28a0abd5a167438d4628938a74ade6f00a44a4b7ed1cfa9cfc57a5b24748 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-arch + value: + - $(params.build-platforms) + name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clamav-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:567cb66bd2e1f4b58b9d4d756f3317fc62479e0b40aa0de66094b1f12d296cfc + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - coverity-availability-check + taskRef: + params: + - name: name + value: sast-coverity-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-coverity-check-oci-ta:0.3@sha256:e92d00ed858233d0096627861192d3e4fc013cf1559c0d0b0ea0657d3377ce75 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + - name: coverity-availability-check + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: coverity-availability-check + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-coverity-availability-check:0.2@sha256:8b501440a960aec446db2ebc6625a49d0317a9fc7bf0f7bd9b18cb63052db7de + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-shell-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:3cbb3535af6e7d4396858179a6427caaffb2e68775594795692fc01f28ae313f + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-unicode-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-unicode-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:223812001607b07f0e07d56bef7b7d619144e660c0c57f21ddd44ce0c8c4785b + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: apply-tags + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: apply-tags + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:a291081de7fb27f832c6fc3c4b078acf7e6162ca4c085db38b118ca87e8b5b66 + - name: kind + value: task + resolver: bundles + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: push-dockerfile-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:7855471abfe87de080b914f2f3ca27c59e64f6448a7c2435e51435b764494c71 + - name: kind + value: task + resolver: bundles + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: rpms-signature-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:237c54b069d16c3785d1302f19be309aa6c0ae2313d446e30cb74671e07ca676 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true + timeouts: + pipeline: 4h + tasks: 4h + taskRunTemplate: + serviceAccountName: build-pipeline-lightspeed-stack-0-5 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/lightspeed-stack-0-5-push.yaml b/.tekton/lightspeed-stack-0-5-push.yaml new file mode 100644 index 000000000..45f3f9e19 --- /dev/null +++ b/.tekton/lightspeed-stack-0-5-push.yaml @@ -0,0 +1,652 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/lightspeed-core/lightspeed-stack?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/cancel-in-progress: "false" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "release/0.5" + labels: + appstudio.openshift.io/application: lightspeed-core-0-5 + appstudio.openshift.io/component: lightspeed-stack-0-5 + pipelines.appstudio.openshift.io/type: build + name: lightspeed-stack-0-5-on-push + namespace: lightspeed-core-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/redhat-user-workloads/lightspeed-core-tenant/lightspeed-stack-0-5:{{revision}} + - name: build-platforms + value: + - linux/x86_64 + - linux-c6gd2xlarge/arm64 + - name: build-source-image + value: 'true' + - name: prefetch-input + value: | + [ + { + "type": "rpm", + "path": ".konflux" + }, + { + "type": "generic", + "path": "." + }, + { + "type": "pip", + "path": ".konflux", + "requirements_files": [ + "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", + "requirements.hashes.source.txt", + "requirements.hermetic.txt" + ], + "requirements_build_files": ["requirements-build.txt"], + "binary": { + "packages": "accelerate,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,cachetools,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,faiss-cpu,fastapi,fastuuid,filelock,fire,frozenlist,fsspec,google-api-core,google-auth,google-cloud-aiplatform,google-cloud-bigquery,google-cloud-core,google-cloud-resource-manager,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,huggingface-hub,idna,importlib-metadata,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,kubernetes,langdetect,litellm,lxml,markdown-it-py,markupsafe,maturin,mcp,mdurl,mpmath,msal,msal-extensions,multidict,multiprocess,networkx,nltk,numpy,oauthlib,openai,opentelemetry-api,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,packaging,pandas,peft,pillow,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,python-dateutil,python-dotenv,python-multipart,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,semver,sentence-transformers,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,typer,typing-extensions,typing-inspection,urllib3,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp", + "os": "linux", + "arch": "x86_64,aarch64", + "py_version": 312 + } + } + ] + - name: hermetic + value: 'true' + - name: dockerfile + value: Containerfile + - name: build-args-file + value: .konflux/build-args-konflux.conf + pipelineSpec: + description: | + This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. + + _Uses `buildah` to create a multi-platform container image leveraging [trusted artifacts](https://konflux-ci.dev/architecture/ADR/0036-trusted-artifacts.html). It also optionally creates a source image and runs some build-time tests. This pipeline requires that the [multi platform controller](https://github.com/konflux-ci/multi-platform-controller) is deployed and configured on your Konflux instance. Information is shared between tasks using OCI artifacts instead of PVCs. EC will pass the [`trusted_task.trusted`](https://conforma.dev/docs/policy/packages/release_trusted_task.html#trusted_task__trusted) policy as long as all data used to build the artifact is generated from trusted tasks. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build-multi-platform-oci-ta?tab=tags)_ + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter path-context + name: dockerfile + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + type: string + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "true" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: docker + description: The format for the resulting image's mediaType. Valid values are oci or docker. + name: buildah-format + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: "false" + description: Whether to enable privileged mode, should be used only with remote VMs + name: privileged-nested + type: string + - default: + - linux/x86_64 + description: List of platforms to build the container images on. The available set of values is determined by the configuration of the multi-platform-controller. + name: build-platforms + type: array + - name: enable-package-registry-proxy + default: 'true' + description: Use the package registry proxy when prefetching dependencies + type: string + - name: sast-target-dirs + type: string + default: . + description: Target directories to scan with SAST tools. Multiple values should be separated with commas. + results: + - description: "" + name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - description: "" + name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - description: "" + name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - description: "" + name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + taskRef: + params: + - name: name + value: init + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:5a423246792ac501ea279229b42ee57da9927da441c04b5c9ff86817b0856b08 + - name: kind + value: task + resolver: bundles + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + params: + - name: name + value: git-clone-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:d30f13dd15daf89dd6dc645243b3444d35570d13f7840c3fd65e366022515205 + - name: kind + value: task + resolver: bundles + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + - name: enable-package-registry-proxy + value: $(params.enable-package-registry-proxy) + runAfter: + - clone-repository + taskRef: + params: + - name: name + value: prefetch-dependencies-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:3dc78afbf3a441e0280067433cb28ea3d2d0088ec214c73bf063f145b4f273ef + - name: kind + value: task + resolver: bundles + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - matrix: + params: + - name: PLATFORM + value: + - $(params.build-platforms) + name: build-images + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: PRIVILEGED_NESTED + value: $(params.privileged-nested) + - name: SOURCE_URL + value: $(tasks.clone-repository.results.url) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: IMAGE_APPEND_PLATFORM + value: "true" + runAfter: + - prefetch-dependencies + taskRef: + params: + - name: name + value: buildah-remote-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:77007259cc87f32d63d2c201226aadaab98313cfd4e02b46abc243c4d2cc27bd + - name: kind + value: task + resolver: bundles + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-images.results.IMAGE_REF[*]) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + runAfter: + - build-images + taskRef: + params: + - name: name + value: build-image-index + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.3@sha256:b33bfa8dc27dbf459f0779598ba45dcaa490bcc9f8efe1652bcf360ec8cb5582 + - name: kind + value: task + resolver: bundles + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: BINARY_IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: source-build-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:8567bb7bf8fa9147c96b297533336fa7079ecf972cb86c09ccdd6bddedb25711 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.build-source-image) + operator: in + values: + - "true" + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: deprecated-image-check + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:e78d0d3baf3c8cfc1a5ad278196b74032d9568b143a87c7a79ab780fedfb296e + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-platform + value: + - $(params.build-platforms) + name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clair-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:8fad4c2e2f470f82ee43d6b2ac72327b4d9c6e9cb514a678911c1c9359c29894 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: platform + value: + - $(params.build-platforms) + name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: ecosystem-cert-preflight-checks + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:88f4fd6d7812a3c46f120f3035974f5fb8cb06b5e3e927badf6e8370f1516a88 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: ARGS + value: --project-name=lightspeed-stack --report --org=dca2ca89-7e51-4a3a-b7a5-6ad5633057b8 + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-snyk-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:0ebf28a0abd5a167438d4628938a74ade6f00a44a4b7ed1cfa9cfc57a5b24748 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-arch + value: + - $(params.build-platforms) + name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clamav-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:567cb66bd2e1f4b58b9d4d756f3317fc62479e0b40aa0de66094b1f12d296cfc + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - coverity-availability-check + taskRef: + params: + - name: name + value: sast-coverity-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-coverity-check-oci-ta:0.3@sha256:e92d00ed858233d0096627861192d3e4fc013cf1559c0d0b0ea0657d3377ce75 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + - name: coverity-availability-check + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: coverity-availability-check + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-coverity-availability-check:0.2@sha256:8b501440a960aec446db2ebc6625a49d0317a9fc7bf0f7bd9b18cb63052db7de + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-shell-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:3cbb3535af6e7d4396858179a6427caaffb2e68775594795692fc01f28ae313f + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-unicode-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-unicode-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:223812001607b07f0e07d56bef7b7d619144e660c0c57f21ddd44ce0c8c4785b + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: apply-tags + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: ADDITIONAL_TAGS + value: + - $(tasks.clone-repository.results.short-commit) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: apply-tags + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:a291081de7fb27f832c6fc3c4b078acf7e6162ca4c085db38b118ca87e8b5b66 + - name: kind + value: task + resolver: bundles + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: push-dockerfile-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:7855471abfe87de080b914f2f3ca27c59e64f6448a7c2435e51435b764494c71 + - name: kind + value: task + resolver: bundles + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: rpms-signature-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:237c54b069d16c3785d1302f19be309aa6c0ae2313d446e30cb74671e07ca676 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true + timeouts: + pipeline: 4h + tasks: 4h + taskRunTemplate: + serviceAccountName: build-pipeline-lightspeed-stack-0-5 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/lightspeed-stack-pull-request.yaml b/.tekton/lightspeed-stack-pull-request.yaml index a98fe68b1..07960f7a9 100644 --- a/.tekton/lightspeed-stack-pull-request.yaml +++ b/.tekton/lightspeed-stack-pull-request.yaml @@ -42,7 +42,7 @@ spec: [ { "type": "rpm", - "path": "." + "path": ".konflux" }, { "type": "generic", @@ -50,15 +50,16 @@ spec: }, { "type": "pip", - "path": ".", + "path": ".konflux", "requirements_files": [ "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", "requirements.hashes.source.txt", "requirements.hermetic.txt" ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,cffi,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,faiss-cpu,fire,frozenlist,fsspec,google-cloud-core,google-crc32c,google-genai,google-resumable-media,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,idna,importlib-metadata,jinja2,jiter,joblib,jsonschema,jsonschema-specifications,kubernetes,lxml,markdown-it-py,mcp,mdurl,mpmath,multidict,networkx,numpy,oauthlib,packaging,pandas,peft,pillow,prometheus-client,prompt-toolkit,propcache,psycopg2-binary,pyarrow,pyasn1-modules,pycparser,pydantic,pydantic-core,pygments,python-dateutil,python-multipart,pyyaml,referencing,requests-oauthlib,rpds-py,safetensors,scikit-learn,scipy,setuptools,six,sniffio,sqlalchemy,sympy,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tqdm,transformers,tree-sitter,triton,typing-extensions,typing-inspection,tzdata,urllib3,websocket-client,websockets,wrapt,xxhash,yarl,zipp,uv,pip,maturin", + "packages": "accelerate,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,cachetools,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,faiss-cpu,fastapi,fastuuid,filelock,fire,frozenlist,fsspec,google-api-core,google-auth,google-cloud-aiplatform,google-cloud-bigquery,google-cloud-core,google-cloud-resource-manager,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,huggingface-hub,idna,importlib-metadata,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,kubernetes,langdetect,litellm,lxml,markdown-it-py,markupsafe,maturin,mcp,mdurl,mpmath,msal,msal-extensions,multidict,multiprocess,networkx,nltk,numpy,oauthlib,openai,opentelemetry-api,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,packaging,pandas,peft,pillow,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,python-dateutil,python-dotenv,python-multipart,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,semver,sentence-transformers,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,typer,typing-extensions,typing-inspection,urllib3,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 @@ -70,7 +71,7 @@ spec: - name: dockerfile value: Containerfile - name: build-args-file - value: build-args-konflux.conf + value: .konflux/build-args-konflux.conf pipelineSpec: description: | This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. @@ -145,6 +146,14 @@ spec: default: 'false' description: Enable cache proxy configuration type: string + - name: enable-package-registry-proxy + default: 'true' + description: Use the package registry proxy when prefetching dependencies + type: string + - name: sast-target-dirs + type: string + default: . + description: Target directories to scan with SAST tools. Multiple values should be separated with commas. results: - description: "" name: IMAGE_URL @@ -168,7 +177,7 @@ spec: - name: name value: init - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:288f3106118edc1d0f0c79a89c960abf5841a4dd8bc3f38feb10527253105b19 + value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:5a423246792ac501ea279229b42ee57da9927da441c04b5c9ff86817b0856b08 - name: kind value: task resolver: bundles @@ -189,7 +198,7 @@ spec: - name: name value: git-clone-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:2c388d28651457db60bb90287e7d8c3680303197196e4476878d98d81e8b6dc9 + value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:d30f13dd15daf89dd6dc645243b3444d35570d13f7840c3fd65e366022515205 - name: kind value: task resolver: bundles @@ -206,6 +215,8 @@ spec: value: $(params.output-image).prefetch - name: ociArtifactExpiresAfter value: $(params.image-expires-after) + - name: enable-package-registry-proxy + value: $(params.enable-package-registry-proxy) runAfter: - clone-repository taskRef: @@ -213,7 +224,7 @@ spec: - name: name value: prefetch-dependencies-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:2229dbc5e15acc0a6d8aec526465aeb0ad54e269c311ac3d0aba88013845e308 + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:3dc78afbf3a441e0280067433cb28ea3d2d0088ec214c73bf063f145b4f273ef - name: kind value: task resolver: bundles @@ -271,7 +282,7 @@ spec: - name: name value: buildah-remote-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:a9ca472e297388d6ef8d1f51ee205abee6076aed7c5356ec0df84f14a2e78ad8 + value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:77007259cc87f32d63d2c201226aadaab98313cfd4e02b46abc243c4d2cc27bd - name: kind value: task resolver: bundles @@ -279,10 +290,6 @@ spec: params: - name: IMAGE value: $(params.output-image) - - name: COMMIT_SHA - value: $(tasks.clone-repository.results.commit) - - name: IMAGE_EXPIRES_AFTER - value: $(params.image-expires-after) - name: ALWAYS_BUILD_INDEX value: $(params.build-image-index) - name: IMAGES @@ -297,7 +304,7 @@ spec: - name: name value: build-image-index - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.2@sha256:c7b0f7e1f743040d99a3532abbdfddc9484f80fd559a75171c97499c3eb5d163 + value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.3@sha256:b33bfa8dc27dbf459f0779598ba45dcaa490bcc9f8efe1652bcf360ec8cb5582 - name: kind value: task resolver: bundles @@ -318,7 +325,7 @@ spec: - name: name value: source-build-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:362f0475df00e7dfb5f15dea0481d1b68b287f60411718d70a23da3c059a5613 + value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:8567bb7bf8fa9147c96b297533336fa7079ecf972cb86c09ccdd6bddedb25711 - name: kind value: task resolver: bundles @@ -340,7 +347,7 @@ spec: - name: name value: deprecated-image-check - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:5ff16b7e6b4a8aa1adb352e74b9f831f77ff97bafd1b89ddb0038d63335f1a67 + value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:e78d0d3baf3c8cfc1a5ad278196b74032d9568b143a87c7a79ab780fedfb296e - name: kind value: task resolver: bundles @@ -367,7 +374,7 @@ spec: - name: name value: clair-scan - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:3fa03be0280f33d7070ea53f26d53e727199737a7a2b9a59a95071ae40a999ac + value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:8fad4c2e2f470f82ee43d6b2ac72327b4d9c6e9cb514a678911c1c9359c29894 - name: kind value: task resolver: bundles @@ -392,7 +399,7 @@ spec: - name: name value: ecosystem-cert-preflight-checks - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:b4ac586edea81dcd25dfc17f1bd57899825be2b443e48d572cd05ce058f153bb + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:88f4fd6d7812a3c46f120f3035974f5fb8cb06b5e3e927badf6e8370f1516a88 - name: kind value: task resolver: bundles @@ -413,6 +420,8 @@ spec: value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) - name: ARGS value: --project-name=lightspeed-stack --report --org=dca2ca89-7e51-4a3a-b7a5-6ad5633057b8 + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - build-image-index taskRef: @@ -420,7 +429,7 @@ spec: - name: name value: sast-snyk-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:d83becbfefe2aa39971c3d37bdc23489b745e22fd86cf4872455a133f8cb274f + value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:0ebf28a0abd5a167438d4628938a74ade6f00a44a4b7ed1cfa9cfc57a5b24748 - name: kind value: task resolver: bundles @@ -447,7 +456,7 @@ spec: - name: name value: clamav-scan - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:9f18b216ce71a66909e7cb17d9b34526c02d73cf12884ba32d1f10614f7b9f5a + value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:567cb66bd2e1f4b58b9d4d756f3317fc62479e0b40aa0de66094b1f12d296cfc - name: kind value: task resolver: bundles @@ -485,6 +494,8 @@ spec: value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) - name: CACHI2_ARTIFACT value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - coverity-availability-check taskRef: @@ -492,7 +503,7 @@ spec: - name: name value: sast-coverity-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-coverity-check-oci-ta:0.3@sha256:47f4e2d0881ac8c43a1ea1e2375bb2591dff34b5aa8c7366a043652d1eed499c + value: quay.io/konflux-ci/tekton-catalog/task-sast-coverity-check-oci-ta:0.3@sha256:e92d00ed858233d0096627861192d3e4fc013cf1559c0d0b0ea0657d3377ce75 - name: kind value: task resolver: bundles @@ -513,7 +524,7 @@ spec: - name: name value: coverity-availability-check - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-coverity-availability-check:0.2@sha256:de35caf2f090e3275cfd1019ea50d9662422e904fb4aebd6ea29fb53a1ad57f5 + value: quay.io/konflux-ci/tekton-catalog/task-coverity-availability-check:0.2@sha256:8b501440a960aec446db2ebc6625a49d0317a9fc7bf0f7bd9b18cb63052db7de - name: kind value: task resolver: bundles @@ -532,6 +543,8 @@ spec: value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) - name: CACHI2_ARTIFACT value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - build-image-index taskRef: @@ -539,7 +552,7 @@ spec: - name: name value: sast-shell-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:6f047f52c04ee6e4d2cb25af46e3ea92b235f6c5e02da540fb7ef0b90718bc0a + value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:3cbb3535af6e7d4396858179a6427caaffb2e68775594795692fc01f28ae313f - name: kind value: task resolver: bundles @@ -558,6 +571,8 @@ spec: value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) - name: CACHI2_ARTIFACT value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - build-image-index taskRef: @@ -565,7 +580,7 @@ spec: - name: name value: sast-unicode-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:55006815522c57c1f83451dc0cba723ff7427dbac48553538b75cda7bf886d79 + value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:223812001607b07f0e07d56bef7b7d619144e660c0c57f21ddd44ce0c8c4785b - name: kind value: task resolver: bundles @@ -587,7 +602,7 @@ spec: - name: name value: apply-tags - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:aa62b41861c09e2e59c69cc6e9a1f740bf0c81e6a1eb03f57f59dfda0f65840e + value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:a291081de7fb27f832c6fc3c4b078acf7e6162ca4c085db38b118ca87e8b5b66 - name: kind value: task resolver: bundles @@ -610,7 +625,7 @@ spec: - name: name value: push-dockerfile-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:1bc2d0f26b89259db090a47bb38217c82c05e335d626653d184adf1d196ca131 + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:7855471abfe87de080b914f2f3ca27c59e64f6448a7c2435e51435b764494c71 - name: kind value: task resolver: bundles @@ -627,7 +642,7 @@ spec: - name: name value: rpms-signature-scan - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:3d48fa2fcc898bae9ce730a71789c34758a767b44bc36fd24938779deef4ee96 + value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:237c54b069d16c3785d1302f19be309aa6c0ae2313d446e30cb74671e07ca676 - name: kind value: task resolver: bundles diff --git a/.tekton/lightspeed-stack-push.yaml b/.tekton/lightspeed-stack-push.yaml index 69f29bad6..4f0588f1e 100644 --- a/.tekton/lightspeed-stack-push.yaml +++ b/.tekton/lightspeed-stack-push.yaml @@ -34,7 +34,7 @@ spec: [ { "type": "rpm", - "path": "." + "path": ".konflux" }, { "type": "generic", @@ -42,15 +42,16 @@ spec: }, { "type": "pip", - "path": ".", + "path": ".konflux", "requirements_files": [ "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", "requirements.hashes.source.txt", "requirements.hermetic.txt" ], "requirements_build_files": ["requirements-build.txt"], "binary": { - "packages": "aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,cffi,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,faiss-cpu,fire,frozenlist,fsspec,google-cloud-core,google-crc32c,google-genai,google-resumable-media,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,idna,importlib-metadata,jinja2,jiter,joblib,jsonschema,jsonschema-specifications,kubernetes,lxml,markdown-it-py,mcp,mdurl,mpmath,multidict,networkx,numpy,oauthlib,packaging,pandas,peft,pillow,prometheus-client,prompt-toolkit,propcache,psycopg2-binary,pyarrow,pyasn1-modules,pycparser,pydantic,pydantic-core,pygments,python-dateutil,python-multipart,pyyaml,referencing,requests-oauthlib,rpds-py,safetensors,scikit-learn,scipy,setuptools,six,sniffio,sqlalchemy,sympy,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tqdm,transformers,tree-sitter,triton,typing-extensions,typing-inspection,tzdata,urllib3,websocket-client,websockets,wrapt,xxhash,yarl,zipp,uv,pip,maturin", + "packages": "accelerate,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,cachetools,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,faiss-cpu,fastapi,fastuuid,filelock,fire,frozenlist,fsspec,google-api-core,google-auth,google-cloud-aiplatform,google-cloud-bigquery,google-cloud-core,google-cloud-resource-manager,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,huggingface-hub,idna,importlib-metadata,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,kubernetes,langdetect,litellm,lxml,markdown-it-py,markupsafe,maturin,mcp,mdurl,mpmath,msal,msal-extensions,multidict,multiprocess,networkx,nltk,numpy,oauthlib,openai,opentelemetry-api,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,packaging,pandas,peft,pillow,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,python-dateutil,python-dotenv,python-multipart,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,semver,sentence-transformers,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,typer,typing-extensions,typing-inspection,urllib3,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp", "os": "linux", "arch": "x86_64,aarch64", "py_version": 312 @@ -62,7 +63,7 @@ spec: - name: dockerfile value: Containerfile - name: build-args-file - value: build-args-konflux.conf + value: .konflux/build-args-konflux.conf pipelineSpec: description: | This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. @@ -133,6 +134,14 @@ spec: description: List of platforms to build the container images on. The available set of values is determined by the configuration of the multi-platform-controller. name: build-platforms type: array + - name: enable-package-registry-proxy + default: 'true' + description: Use the package registry proxy when prefetching dependencies + type: string + - name: sast-target-dirs + type: string + default: . + description: Target directories to scan with SAST tools. Multiple values should be separated with commas. results: - description: "" name: IMAGE_URL @@ -153,7 +162,7 @@ spec: - name: name value: init - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:288f3106118edc1d0f0c79a89c960abf5841a4dd8bc3f38feb10527253105b19 + value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:5a423246792ac501ea279229b42ee57da9927da441c04b5c9ff86817b0856b08 - name: kind value: task resolver: bundles @@ -174,7 +183,7 @@ spec: - name: name value: git-clone-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:2c388d28651457db60bb90287e7d8c3680303197196e4476878d98d81e8b6dc9 + value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:d30f13dd15daf89dd6dc645243b3444d35570d13f7840c3fd65e366022515205 - name: kind value: task resolver: bundles @@ -191,6 +200,8 @@ spec: value: $(params.output-image).prefetch - name: ociArtifactExpiresAfter value: $(params.image-expires-after) + - name: enable-package-registry-proxy + value: $(params.enable-package-registry-proxy) runAfter: - clone-repository taskRef: @@ -198,7 +209,7 @@ spec: - name: name value: prefetch-dependencies-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:2229dbc5e15acc0a6d8aec526465aeb0ad54e269c311ac3d0aba88013845e308 + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:3dc78afbf3a441e0280067433cb28ea3d2d0088ec214c73bf063f145b4f273ef - name: kind value: task resolver: bundles @@ -252,7 +263,7 @@ spec: - name: name value: buildah-remote-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:a9ca472e297388d6ef8d1f51ee205abee6076aed7c5356ec0df84f14a2e78ad8 + value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:77007259cc87f32d63d2c201226aadaab98313cfd4e02b46abc243c4d2cc27bd - name: kind value: task resolver: bundles @@ -260,10 +271,6 @@ spec: params: - name: IMAGE value: $(params.output-image) - - name: COMMIT_SHA - value: $(tasks.clone-repository.results.commit) - - name: IMAGE_EXPIRES_AFTER - value: $(params.image-expires-after) - name: ALWAYS_BUILD_INDEX value: $(params.build-image-index) - name: IMAGES @@ -278,7 +285,7 @@ spec: - name: name value: build-image-index - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.2@sha256:c7b0f7e1f743040d99a3532abbdfddc9484f80fd559a75171c97499c3eb5d163 + value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.3@sha256:b33bfa8dc27dbf459f0779598ba45dcaa490bcc9f8efe1652bcf360ec8cb5582 - name: kind value: task resolver: bundles @@ -299,7 +306,7 @@ spec: - name: name value: source-build-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:362f0475df00e7dfb5f15dea0481d1b68b287f60411718d70a23da3c059a5613 + value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:8567bb7bf8fa9147c96b297533336fa7079ecf972cb86c09ccdd6bddedb25711 - name: kind value: task resolver: bundles @@ -321,7 +328,7 @@ spec: - name: name value: deprecated-image-check - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:5ff16b7e6b4a8aa1adb352e74b9f831f77ff97bafd1b89ddb0038d63335f1a67 + value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:e78d0d3baf3c8cfc1a5ad278196b74032d9568b143a87c7a79ab780fedfb296e - name: kind value: task resolver: bundles @@ -348,7 +355,7 @@ spec: - name: name value: clair-scan - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:3fa03be0280f33d7070ea53f26d53e727199737a7a2b9a59a95071ae40a999ac + value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:8fad4c2e2f470f82ee43d6b2ac72327b4d9c6e9cb514a678911c1c9359c29894 - name: kind value: task resolver: bundles @@ -373,7 +380,7 @@ spec: - name: name value: ecosystem-cert-preflight-checks - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:b4ac586edea81dcd25dfc17f1bd57899825be2b443e48d572cd05ce058f153bb + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:88f4fd6d7812a3c46f120f3035974f5fb8cb06b5e3e927badf6e8370f1516a88 - name: kind value: task resolver: bundles @@ -394,6 +401,8 @@ spec: value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) - name: ARGS value: --project-name=lightspeed-stack --report --org=dca2ca89-7e51-4a3a-b7a5-6ad5633057b8 + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - build-image-index taskRef: @@ -401,7 +410,7 @@ spec: - name: name value: sast-snyk-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:d83becbfefe2aa39971c3d37bdc23489b745e22fd86cf4872455a133f8cb274f + value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:0ebf28a0abd5a167438d4628938a74ade6f00a44a4b7ed1cfa9cfc57a5b24748 - name: kind value: task resolver: bundles @@ -428,7 +437,7 @@ spec: - name: name value: clamav-scan - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:9f18b216ce71a66909e7cb17d9b34526c02d73cf12884ba32d1f10614f7b9f5a + value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:567cb66bd2e1f4b58b9d4d756f3317fc62479e0b40aa0de66094b1f12d296cfc - name: kind value: task resolver: bundles @@ -466,6 +475,8 @@ spec: value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) - name: CACHI2_ARTIFACT value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - coverity-availability-check taskRef: @@ -473,7 +484,7 @@ spec: - name: name value: sast-coverity-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-coverity-check-oci-ta:0.3@sha256:47f4e2d0881ac8c43a1ea1e2375bb2591dff34b5aa8c7366a043652d1eed499c + value: quay.io/konflux-ci/tekton-catalog/task-sast-coverity-check-oci-ta:0.3@sha256:e92d00ed858233d0096627861192d3e4fc013cf1559c0d0b0ea0657d3377ce75 - name: kind value: task resolver: bundles @@ -494,7 +505,7 @@ spec: - name: name value: coverity-availability-check - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-coverity-availability-check:0.2@sha256:de35caf2f090e3275cfd1019ea50d9662422e904fb4aebd6ea29fb53a1ad57f5 + value: quay.io/konflux-ci/tekton-catalog/task-coverity-availability-check:0.2@sha256:8b501440a960aec446db2ebc6625a49d0317a9fc7bf0f7bd9b18cb63052db7de - name: kind value: task resolver: bundles @@ -513,6 +524,8 @@ spec: value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) - name: CACHI2_ARTIFACT value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - build-image-index taskRef: @@ -520,7 +533,7 @@ spec: - name: name value: sast-shell-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:6f047f52c04ee6e4d2cb25af46e3ea92b235f6c5e02da540fb7ef0b90718bc0a + value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:3cbb3535af6e7d4396858179a6427caaffb2e68775594795692fc01f28ae313f - name: kind value: task resolver: bundles @@ -539,6 +552,8 @@ spec: value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) - name: CACHI2_ARTIFACT value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) runAfter: - build-image-index taskRef: @@ -546,7 +561,7 @@ spec: - name: name value: sast-unicode-check-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:55006815522c57c1f83451dc0cba723ff7427dbac48553538b75cda7bf886d79 + value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:223812001607b07f0e07d56bef7b7d619144e660c0c57f21ddd44ce0c8c4785b - name: kind value: task resolver: bundles @@ -571,7 +586,7 @@ spec: - name: name value: apply-tags - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:aa62b41861c09e2e59c69cc6e9a1f740bf0c81e6a1eb03f57f59dfda0f65840e + value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:a291081de7fb27f832c6fc3c4b078acf7e6162ca4c085db38b118ca87e8b5b66 - name: kind value: task resolver: bundles @@ -594,7 +609,7 @@ spec: - name: name value: push-dockerfile-oci-ta - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:1bc2d0f26b89259db090a47bb38217c82c05e335d626653d184adf1d196ca131 + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:7855471abfe87de080b914f2f3ca27c59e64f6448a7c2435e51435b764494c71 - name: kind value: task resolver: bundles @@ -611,7 +626,7 @@ spec: - name: name value: rpms-signature-scan - name: bundle - value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:3d48fa2fcc898bae9ce730a71789c34758a767b44bc36fd24938779deef4ee96 + value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:237c54b069d16c3785d1302f19be309aa6c0ae2313d446e30cb74671e07ca676 - name: kind value: task resolver: bundles diff --git a/.tekton/lightspeed-stack-release-0-5-pull-request.yaml b/.tekton/lightspeed-stack-release-0-5-pull-request.yaml new file mode 100644 index 000000000..a9de09faf --- /dev/null +++ b/.tekton/lightspeed-stack-release-0-5-pull-request.yaml @@ -0,0 +1,603 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/lightspeed-core/lightspeed-stack?rev={{revision}} + build.appstudio.redhat.com/commit_sha: "{{revision}}" + build.appstudio.redhat.com/pull_request_number: "{{pull_request_number}}" + build.appstudio.redhat.com/target_branch: "{{target_branch}}" + pipelinesascode.tekton.dev/cancel-in-progress: "true" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: + event == "pull_request" && target_branch + == "release/0.5" + creationTimestamp: null + labels: + appstudio.openshift.io/application: lightspeed-stack + appstudio.openshift.io/component: lightspeed-stack-release-0-5 + pipelines.appstudio.openshift.io/type: build + name: lightspeed-stack-release-0-5-on-pull-request + namespace: lightspeed-core-tenant +spec: + params: + - name: git-url + value: "{{source_url}}" + - name: revision + value: "{{revision}}" + - name: output-image + value: quay.io/redhat-user-workloads/lightspeed-core-tenant/lightspeed-stack-release-0-5:on-pr-{{revision}} + - name: image-expires-after + value: 5d + - name: build-platforms + value: + - linux/x86_64 + - linux-c6gd2xlarge/arm64 + - name: build-source-image + value: "true" + - name: prefetch-input + value: | + [ + { + "type": "rpm", + "path": ".konflux" + }, + { + "type": "generic", + "path": "." + }, + { + "type": "pip", + "path": ".konflux", + "requirements_files": [ + "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", + "requirements.hashes.source.txt", + "requirements.hermetic.txt" + ], + "requirements_build_files": ["requirements-build.txt"], + "binary": { + "packages": "accelerate,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,cachetools,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,faiss-cpu,fastapi,fastuuid,filelock,fire,frozenlist,fsspec,google-api-core,google-auth,google-cloud-aiplatform,google-cloud-bigquery,google-cloud-core,google-cloud-resource-manager,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,huggingface-hub,idna,importlib-metadata,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,kubernetes,langdetect,litellm,lxml,markdown-it-py,markupsafe,maturin,mcp,mdurl,mpmath,msal,msal-extensions,multidict,multiprocess,networkx,nltk,numpy,oauthlib,openai,opentelemetry-api,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,packaging,pandas,peft,pillow,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,python-dateutil,python-dotenv,python-multipart,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,semver,sentence-transformers,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,typer,typing-extensions,typing-inspection,urllib3,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp", + "os": "linux", + "arch": "x86_64,aarch64", + "py_version": 312 + } + } + ] + - name: hermetic + value: "true" + - name: dockerfile + value: Containerfile + - name: build-args-file + value: .konflux/build-args-konflux.conf + pipelineSpec: + description: | + This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. + + _Uses `buildah` to create a multi-platform container image leveraging [trusted artifacts](https://konflux-ci.dev/architecture/ADR/0036-trusted-artifacts.html). It also optionally creates a source image and runs some build-time tests. This pipeline requires that the [multi platform controller](https://github.com/konflux-ci/multi-platform-controller) is deployed and configured on your Konflux instance. Information is shared between tasks using OCI artifacts instead of PVCs. EC will pass the [`trusted_task.trusted`](https://conforma.dev/docs/policy/packages/release_trusted_task.html#trusted_task__trusted) policy as long as all data used to build the artifact is generated from trusted tasks. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build-multi-platform-oci-ta?tab=tags)_ + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: + Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: + Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched + name: prefetch-input + type: string + - default: "" + description: + Image tag expiration time, time values could be something like + 1h, 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + type: string + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "true" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: docker + description: + The format for the resulting image's mediaType. Valid values are + oci or docker. + name: buildah-format + type: string + - default: "false" + description: Enable cache proxy configuration + name: enable-cache-proxy + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: "false" + description: + Whether to enable privileged mode, should be used only with remote + VMs + name: privileged-nested + type: string + - default: + - linux/x86_64 + description: + List of platforms to build the container images on. The available + set of values is determined by the configuration of the multi-platform-controller. + name: build-platforms + type: array + - name: enable-package-registry-proxy + default: 'true' + description: Use the package registry proxy when prefetching dependencies + type: string + - name: sast-target-dirs + type: string + default: . + description: Target directories to scan with SAST tools. Multiple values should be separated with commas. + results: + - description: "" + name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - description: "" + name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - description: "" + name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - description: "" + name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: enable-cache-proxy + value: $(params.enable-cache-proxy) + taskRef: + params: + - name: name + value: init + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:5a423246792ac501ea279229b42ee57da9927da441c04b5c9ff86817b0856b08 + - name: kind + value: task + resolver: bundles + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + params: + - name: name + value: git-clone-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:d30f13dd15daf89dd6dc645243b3444d35570d13f7840c3fd65e366022515205 + - name: kind + value: task + resolver: bundles + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + - name: enable-package-registry-proxy + value: $(params.enable-package-registry-proxy) + runAfter: + - clone-repository + taskRef: + params: + - name: name + value: prefetch-dependencies-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:3dc78afbf3a441e0280067433cb28ea3d2d0088ec214c73bf063f145b4f273ef + - name: kind + value: task + resolver: bundles + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - matrix: + params: + - name: PLATFORM + value: + - $(params.build-platforms) + name: build-images + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: PRIVILEGED_NESTED + value: $(params.privileged-nested) + - name: SOURCE_URL + value: $(tasks.clone-repository.results.url) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + - name: HTTP_PROXY + value: $(tasks.init.results.http-proxy) + - name: NO_PROXY + value: $(tasks.init.results.no-proxy) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: IMAGE_APPEND_PLATFORM + value: "true" + runAfter: + - prefetch-dependencies + taskRef: + params: + - name: name + value: buildah-remote-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:77007259cc87f32d63d2c201226aadaab98313cfd4e02b46abc243c4d2cc27bd + - name: kind + value: task + resolver: bundles + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-images.results.IMAGE_REF[*]) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + runAfter: + - build-images + taskRef: + params: + - name: name + value: build-image-index + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.3@sha256:b33bfa8dc27dbf459f0779598ba45dcaa490bcc9f8efe1652bcf360ec8cb5582 + - name: kind + value: task + resolver: bundles + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: BINARY_IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: source-build-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:8567bb7bf8fa9147c96b297533336fa7079ecf972cb86c09ccdd6bddedb25711 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.build-source-image) + operator: in + values: + - "true" + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: deprecated-image-check + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:e78d0d3baf3c8cfc1a5ad278196b74032d9568b143a87c7a79ab780fedfb296e + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-platform + value: + - $(params.build-platforms) + name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clair-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:8fad4c2e2f470f82ee43d6b2ac72327b4d9c6e9cb514a678911c1c9359c29894 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: platform + value: + - $(params.build-platforms) + name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: ecosystem-cert-preflight-checks + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:88f4fd6d7812a3c46f120f3035974f5fb8cb06b5e3e927badf6e8370f1516a88 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-snyk-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:0ebf28a0abd5a167438d4628938a74ade6f00a44a4b7ed1cfa9cfc57a5b24748 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-arch + value: + - $(params.build-platforms) + name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clamav-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:567cb66bd2e1f4b58b9d4d756f3317fc62479e0b40aa0de66094b1f12d296cfc + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-shell-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:3cbb3535af6e7d4396858179a6427caaffb2e68775594795692fc01f28ae313f + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-unicode-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-unicode-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:223812001607b07f0e07d56bef7b7d619144e660c0c57f21ddd44ce0c8c4785b + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: apply-tags + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: apply-tags + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:a291081de7fb27f832c6fc3c4b078acf7e6162ca4c085db38b118ca87e8b5b66 + - name: kind + value: task + resolver: bundles + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: push-dockerfile-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:7855471abfe87de080b914f2f3ca27c59e64f6448a7c2435e51435b764494c71 + - name: kind + value: task + resolver: bundles + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: rpms-signature-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:237c54b069d16c3785d1302f19be309aa6c0ae2313d446e30cb74671e07ca676 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true + taskRunTemplate: + serviceAccountName: build-pipeline-lightspeed-stack-release-0-5 + workspaces: + - name: git-auth + secret: + secretName: "{{ git_auth_secret }}" +status: {} diff --git a/.tekton/lightspeed-stack-release-0-5-push.yaml b/.tekton/lightspeed-stack-release-0-5-push.yaml new file mode 100644 index 000000000..b50c93a05 --- /dev/null +++ b/.tekton/lightspeed-stack-release-0-5-push.yaml @@ -0,0 +1,600 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/lightspeed-core/lightspeed-stack?rev={{revision}} + build.appstudio.redhat.com/commit_sha: "{{revision}}" + build.appstudio.redhat.com/target_branch: "{{target_branch}}" + pipelinesascode.tekton.dev/cancel-in-progress: "false" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: + event == "push" && target_branch + == "release/0.5" + creationTimestamp: null + labels: + appstudio.openshift.io/application: lightspeed-stack + appstudio.openshift.io/component: lightspeed-stack-release-0-5 + pipelines.appstudio.openshift.io/type: build + name: lightspeed-stack-release-0-5-on-push + namespace: lightspeed-core-tenant +spec: + params: + - name: git-url + value: "{{source_url}}" + - name: revision + value: "{{revision}}" + - name: output-image + value: quay.io/redhat-user-workloads/lightspeed-core-tenant/lightspeed-stack-release-0-5:{{revision}} + - name: build-platforms + value: + - linux/x86_64 + - linux-c6gd2xlarge/arm64 + - name: dockerfile + value: Containerfile + - name: build-source-image + value: "true" + - name: prefetch-input + value: | + [ + { + "type": "rpm", + "path": ".konflux" + }, + { + "type": "generic", + "path": "." + }, + { + "type": "pip", + "path": ".konflux", + "requirements_files": [ + "requirements.hashes.wheel.txt", + "requirements.hashes.wheel.pypi.txt", + "requirements.hashes.source.txt", + "requirements.hermetic.txt" + ], + "requirements_build_files": ["requirements-build.txt"], + "binary": { + "packages": "accelerate,aiohappyeyeballs,aiohttp,aiosignal,aiosqlite,annotated-doc,annotated-types,anyio,asyncpg,attrs,authlib,autoevals,azure-core,azure-identity,cachetools,certifi,cffi,chardet,charset-normalizer,chevron,click,cryptography,datasets,dill,distro,dnspython,docstring-parser,durationpy,einops,email-validator,emoji,faiss-cpu,fastapi,fastuuid,filelock,fire,frozenlist,fsspec,google-api-core,google-auth,google-cloud-aiplatform,google-cloud-bigquery,google-cloud-core,google-cloud-resource-manager,google-cloud-storage,google-crc32c,google-genai,google-resumable-media,googleapis-common-protos,greenlet,grpc-google-iam-v1,grpcio,grpcio-status,h11,hf-xet,httpcore,httpx,httpx-sse,huggingface-hub,idna,importlib-metadata,jinja2,jiter,joblib,joserfc,jsonpath-ng,jsonschema,jsonschema-specifications,kubernetes,langdetect,litellm,lxml,markdown-it-py,markupsafe,maturin,mcp,mdurl,mpmath,msal,msal-extensions,multidict,multiprocess,networkx,nltk,numpy,oauthlib,openai,opentelemetry-api,opentelemetry-exporter-otlp,opentelemetry-exporter-otlp-proto-common,opentelemetry-exporter-otlp-proto-grpc,opentelemetry-exporter-otlp-proto-http,opentelemetry-instrumentation,opentelemetry-proto,opentelemetry-sdk,opentelemetry-semantic-conventions,packaging,pandas,peft,pillow,polyleven,prometheus-client,prompt-toolkit,propcache,proto-plus,protobuf,psutil,psycopg2-binary,pyaml,pyarrow,pyasn1,pyasn1-modules,pycparser,pydantic,pydantic-core,pydantic-settings,pygments,pyjwt,python-dateutil,python-dotenv,python-multipart,pyyaml,referencing,regex,requests,requests-oauthlib,rich,rpds-py,safetensors,scikit-learn,scipy,semver,sentence-transformers,setuptools,shellingham,six,sniffio,sqlalchemy,sse-starlette,starlette,sympy,tenacity,termcolor,threadpoolctl,tiktoken,tokenizers,torch,tornado,tqdm,transformers,tree-sitter,triton,trl,typer,typing-extensions,typing-inspection,urllib3,uvicorn,wcwidth,websocket-client,websockets,wrapt,xxhash,yarl,zipp", + "os": "linux", + "arch": "x86_64,aarch64", + "py_version": 312 + } + } + ] + - name: hermetic + value: "true" + - name: build-args-file + value: .konflux/build-args-konflux.conf + pipelineSpec: + description: | + This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. + + _Uses `buildah` to create a multi-platform container image leveraging [trusted artifacts](https://konflux-ci.dev/architecture/ADR/0036-trusted-artifacts.html). It also optionally creates a source image and runs some build-time tests. This pipeline requires that the [multi platform controller](https://github.com/konflux-ci/multi-platform-controller) is deployed and configured on your Konflux instance. Information is shared between tasks using OCI artifacts instead of PVCs. EC will pass the [`trusted_task.trusted`](https://conforma.dev/docs/policy/packages/release_trusted_task.html#trusted_task__trusted) policy as long as all data used to build the artifact is generated from trusted tasks. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build-multi-platform-oci-ta?tab=tags)_ + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: + Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: + Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched + name: prefetch-input + type: string + - default: "" + description: + Image tag expiration time, time values could be something like + 1h, 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + type: string + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "true" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: docker + description: + The format for the resulting image's mediaType. Valid values are + oci or docker. + name: buildah-format + type: string + - default: "false" + description: Enable cache proxy configuration + name: enable-cache-proxy + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: "false" + description: + Whether to enable privileged mode, should be used only with remote + VMs + name: privileged-nested + type: string + - default: + - linux/x86_64 + description: + List of platforms to build the container images on. The available + set of values is determined by the configuration of the multi-platform-controller. + name: build-platforms + type: array + - name: enable-package-registry-proxy + default: 'true' + description: Use the package registry proxy when prefetching dependencies + type: string + - name: sast-target-dirs + type: string + default: . + description: Target directories to scan with SAST tools. Multiple values should be separated with commas. + results: + - description: "" + name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - description: "" + name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - description: "" + name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - description: "" + name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: enable-cache-proxy + value: $(params.enable-cache-proxy) + taskRef: + params: + - name: name + value: init + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-init:0.4@sha256:5a423246792ac501ea279229b42ee57da9927da441c04b5c9ff86817b0856b08 + - name: kind + value: task + resolver: bundles + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + params: + - name: name + value: git-clone-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-git-clone-oci-ta:0.1@sha256:d30f13dd15daf89dd6dc645243b3444d35570d13f7840c3fd65e366022515205 + - name: kind + value: task + resolver: bundles + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + - name: enable-package-registry-proxy + value: $(params.enable-package-registry-proxy) + runAfter: + - clone-repository + taskRef: + params: + - name: name + value: prefetch-dependencies-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies-oci-ta:0.3@sha256:3dc78afbf3a441e0280067433cb28ea3d2d0088ec214c73bf063f145b4f273ef + - name: kind + value: task + resolver: bundles + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - matrix: + params: + - name: PLATFORM + value: + - $(params.build-platforms) + name: build-images + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: PRIVILEGED_NESTED + value: $(params.privileged-nested) + - name: SOURCE_URL + value: $(tasks.clone-repository.results.url) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + - name: HTTP_PROXY + value: $(tasks.init.results.http-proxy) + - name: NO_PROXY + value: $(tasks.init.results.no-proxy) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: IMAGE_APPEND_PLATFORM + value: "true" + runAfter: + - prefetch-dependencies + taskRef: + params: + - name: name + value: buildah-remote-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-buildah-remote-oci-ta:0.9@sha256:77007259cc87f32d63d2c201226aadaab98313cfd4e02b46abc243c4d2cc27bd + - name: kind + value: task + resolver: bundles + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-images.results.IMAGE_REF[*]) + - name: BUILDAH_FORMAT + value: $(params.buildah-format) + runAfter: + - build-images + taskRef: + params: + - name: name + value: build-image-index + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-build-image-index:0.3@sha256:b33bfa8dc27dbf459f0779598ba45dcaa490bcc9f8efe1652bcf360ec8cb5582 + - name: kind + value: task + resolver: bundles + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: BINARY_IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: source-build-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-source-build-oci-ta:0.3@sha256:8567bb7bf8fa9147c96b297533336fa7079ecf972cb86c09ccdd6bddedb25711 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.build-source-image) + operator: in + values: + - "true" + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: deprecated-image-check + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.5@sha256:e78d0d3baf3c8cfc1a5ad278196b74032d9568b143a87c7a79ab780fedfb296e + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-platform + value: + - $(params.build-platforms) + name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clair-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.3@sha256:8fad4c2e2f470f82ee43d6b2ac72327b4d9c6e9cb514a678911c1c9359c29894 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: platform + value: + - $(params.build-platforms) + name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: ecosystem-cert-preflight-checks + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-ecosystem-cert-preflight-checks:0.2@sha256:88f4fd6d7812a3c46f120f3035974f5fb8cb06b5e3e927badf6e8370f1516a88 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-snyk-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check-oci-ta:0.4@sha256:0ebf28a0abd5a167438d4628938a74ade6f00a44a4b7ed1cfa9cfc57a5b24748 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - matrix: + params: + - name: image-arch + value: + - $(params.build-platforms) + name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: clamav-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.3@sha256:567cb66bd2e1f4b58b9d4d756f3317fc62479e0b40aa0de66094b1f12d296cfc + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-shell-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-shell-check-oci-ta:0.1@sha256:3cbb3535af6e7d4396858179a6427caaffb2e68775594795692fc01f28ae313f + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-unicode-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: TARGET_DIRS + value: $(params.sast-target-dirs) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: sast-unicode-check-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-sast-unicode-check-oci-ta:0.4@sha256:223812001607b07f0e07d56bef7b7d619144e660c0c57f21ddd44ce0c8c4785b + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: apply-tags + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: apply-tags + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.3@sha256:a291081de7fb27f832c6fc3c4b078acf7e6162ca4c085db38b118ca87e8b5b66 + - name: kind + value: task + resolver: bundles + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: push-dockerfile-oci-ta + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-push-dockerfile-oci-ta:0.3@sha256:7855471abfe87de080b914f2f3ca27c59e64f6448a7c2435e51435b764494c71 + - name: kind + value: task + resolver: bundles + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + params: + - name: name + value: rpms-signature-scan + - name: bundle + value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:237c54b069d16c3785d1302f19be309aa6c0ae2313d446e30cb74671e07ca676 + - name: kind + value: task + resolver: bundles + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true + taskRunTemplate: + serviceAccountName: build-pipeline-lightspeed-stack-release-0-5 + workspaces: + - name: git-auth + secret: + secretName: "{{ git_auth_secret }}" +status: {} diff --git a/Containerfile b/Containerfile index 134604471..199c16aec 100644 --- a/Containerfile +++ b/Containerfile @@ -24,7 +24,8 @@ USER root # Install gcc - required by polyleven python package on aarch64 # (dependency of autoevals, no pre-built binary wheels for linux on aarch64) # cmake and cargo are required by fastuuid, maturin -RUN ${BUILDER_DNF_COMMAND} install -y --nodocs --setopt=keepcache=0 --setopt=tsflags=nodocs gcc gcc-c++ cmake cargo +RUN ${BUILDER_DNF_COMMAND} install -y --nodocs --setopt=keepcache=0 --setopt=tsflags=nodocs gcc gcc-c++ cmake cargo && \ + ${BUILDER_DNF_COMMAND} update -y --nodocs --setopt=keepcache=0 --setopt=tsflags=nodocs gnutls libxslt openssl-fips-provider # Install uv package manager RUN pip3.12 install "uv>=0.8.15" @@ -32,7 +33,7 @@ RUN pip3.12 install "uv>=0.8.15" # Add explicit files and directories # (avoid accidental inclusion of local directories or env files or credentials) COPY ${LSC_SOURCE_DIR}/src ./src -COPY ${LSC_SOURCE_DIR}/pyproject.toml ${LSC_SOURCE_DIR}/LICENSE ${LSC_SOURCE_DIR}/README.md ${LSC_SOURCE_DIR}/uv.lock ${LSC_SOURCE_DIR}/requirements.*.txt ./ +COPY ${LSC_SOURCE_DIR}/pyproject.toml ${LSC_SOURCE_DIR}/LICENSE ${LSC_SOURCE_DIR}/README.md ${LSC_SOURCE_DIR}/uv.lock ${LSC_SOURCE_DIR}/.konflux/requirements.*.txt ./ # lightspeed-providers: # Fully hermetic — uses prefetched artifact or pinned commit from GitHub @@ -68,10 +69,12 @@ RUN set -eux; \ # PIP_NO_INDEX=true RUN if [ -f /cachi2/cachi2.env ]; then \ . /cachi2/cachi2.env && \ - uv venv --seed --no-index --find-links ${PIP_FIND_LINKS} && \ + python3.12 -c "import os,re;d=os.environ['PIP_FIND_LINKS'];fs=os.listdir(d);rp={re.split(r'-\d+-(?:cp|py|pp)',f)[0] for f in fs if f.endswith('.whl') and re.search(r'-\d+-(?:cp|py|pp)',f)};[os.remove(os.path.join(d,f)) for f in fs if f.endswith('.whl') and not re.search(r'-\d+-(?:cp|py|pp)',f) and f.rsplit('-',3)[0] in rp]" && \ + uv venv --python python3.12 && \ . .venv/bin/activate && \ - pip install --no-cache-dir --ignore-installed --no-index --find-links ${PIP_FIND_LINKS} --no-deps -r requirements.hashes.wheel.txt -r requirements.hashes.source.txt && \ - pip check; \ + sed -i '/^--index-url/d' requirements.hashes.wheel.txt requirements.hashes.wheel.pypi.txt requirements.hashes.source.txt && \ + uv pip install --no-cache --reinstall --no-index --find-links ${PIP_FIND_LINKS} --no-deps -r requirements.hashes.wheel.txt -r requirements.hashes.wheel.pypi.txt -r requirements.hashes.source.txt && \ + uv pip check; \ else \ uv sync --locked --no-dev --group llslibdev; \ fi @@ -113,7 +116,8 @@ COPY --from=builder /app-root/LICENSE /licenses/ USER root # Additional tools for derived images -RUN ${RUNTIME_DNF_COMMAND} install -y --nodocs --setopt=keepcache=0 --setopt=tsflags=nodocs jq patch +RUN ${RUNTIME_DNF_COMMAND} install -y --nodocs --setopt=keepcache=0 --setopt=tsflags=nodocs jq patch && \ + ${RUNTIME_DNF_COMMAND} update -y --nodocs --setopt=keepcache=0 --setopt=tsflags=nodocs gnutls libxslt openssl-fips-provider # Create llama-stack directories for library mode RUN mkdir -p /opt/app-root/src/.llama/storage /opt/app-root/src/.llama/providers.d && \ @@ -137,7 +141,7 @@ ENTRYPOINT ["python3.12", "src/lightspeed_stack.py"] LABEL vendor="Red Hat, Inc." \ name="lightspeed-core/lightspeed-stack-rhel9" \ com.redhat.component="lightspeed-core/lightspeed-stack" \ - cpe="cpe:/a:redhat:lightspeed_core:0.4::el9" \ + cpe="cpe:/a:redhat:lightspeed_core:0.5::el9" \ io.k8s.display-name="Lightspeed Stack" \ summary="A service that provides a REST API for the Lightspeed Core Stack." \ description="Lightspeed Core Stack (LCS) is an AI-powered assistant that provides answers to product questions using backend LLM services, agents, and RAG databases." \ diff --git a/Makefile b/Makefile index c01f93489..2de2b9e2e 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,15 @@ test-e2e: ## Run end to end tests for the service test-e2e-local: ## Run end to end tests for the service uv run behave --color --format pretty --tags=-skip -D dump_errors=true @tests/e2e/test_list.txt +# E2E_BEHAVE_TAG_EXPR='not @skip and @e2e_group_2' make test-e2e-tagged-local +E2E_BEHAVE_TAG_EXPR ?= not @skip and (e2e_group_1 or e2e_group_2 or e2e_group_3) + +test-e2e-tagged: ## Run e2e tests with E2E_BEHAVE_TAG_EXPR (default: all @e2e_group_*) + script -q -e -c "uv run behave --color --format pretty --tags=\"$(E2E_BEHAVE_TAG_EXPR)\" -D dump_errors=true @tests/e2e/test_list.txt" + +test-e2e-tagged-local: ## Same as test-e2e-tagged without script wrapper + uv run behave --color --format pretty --tags="$(E2E_BEHAVE_TAG_EXPR)" -D dump_errors=true @tests/e2e/test_list.txt + benchmarks: ## Run benchmarks uv run python -m pytest -vv tests/benchmarks/ @@ -119,7 +128,7 @@ upload-distribution-archives: ## Upload distribution archives into Python regist uv run python -m twine upload --repository ${PYTHON_REGISTRY} dist/* konflux-requirements: ## Generate hermetic requirements.*.txt file for konflux build - ./scripts/konflux_requirements.sh + python3 scripts/konflux_resolve.py --profile cpu konflux-rpm-lock: ## Generate rpm.lock.yaml file for konflux build ./scripts/generate-rpm-lock.sh diff --git a/README.md b/README.md index d14954828..27b08c8ac 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![License](https://img.shields.io/badge/license-Apache-blue)](https://github.com/lightspeed-core/lightspeed-stack/blob/main/LICENSE) [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/) [![Required Python version](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Flightspeed-core%2Flightspeed-stack%2Frefs%2Fheads%2Fmain%2Fpyproject.toml)](https://www.python.org/) -[![Tag](https://img.shields.io/github/v/tag/lightspeed-core/lightspeed-stack)](https://github.com/lightspeed-core/lightspeed-stack/releases/tag/0.5.0) +[![Tag](https://img.shields.io/github/v/tag/lightspeed-core/lightspeed-stack)](https://github.com/lightspeed-core/lightspeed-stack/releases/tag/0.5.1) Lightspeed Core Stack (LCS) is an AI-powered assistant that provides answers to product questions using backend LLM services, agents, and RAG databases. diff --git a/build-args-konflux.conf b/build-args-konflux.conf deleted file mode 100644 index 18b2697ee..000000000 --- a/build-args-konflux.conf +++ /dev/null @@ -1,4 +0,0 @@ -BUILDER_BASE_IMAGE=registry.redhat.io/rhai/base-image-cpu-rhel9:3.2 -BUILDER_DNF_COMMAND=dnf -RUNTIME_BASE_IMAGE=registry.redhat.io/rhai/base-image-cpu-rhel9:3.2 -RUNTIME_DNF_COMMAND=dnf diff --git a/docs/openapi.json b/docs/openapi.json index f24a1a545..a83093ce0 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -13,7 +13,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "version": "0.5.0" + "version": "0.5.1" }, "servers": [ { diff --git a/docs/splunk.md b/docs/splunk.md index 65de8fbbd..4b23908f8 100644 --- a/docs/splunk.md +++ b/docs/splunk.md @@ -85,7 +85,7 @@ Events follow the rlsapi telemetry format for consistency with existing analytic "system_id": "abc-def-123", "total_llm_tokens": 0, "request_id": "req_xyz789", - "cla_version": "CLA/0.5.0", + "cla_version": "CLA/0.5.1", "system_os": "RHEL", "system_version": "9.3", "system_arch": "x86_64" diff --git a/pyproject.toml b/pyproject.toml index fe5c74f56..fc37d96b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,9 +28,9 @@ dependencies = [ # Used by authentication/k8s integration "kubernetes>=30.1.0", # Used to call Llama Stack APIs - "llama-stack==0.5.2", - "llama-stack-client==0.5.2", - "llama-stack-api==0.5.2", + "llama-stack==0.5.4", + "llama-stack-client==0.5.4", + "llama-stack-api==0.5.4", # Used by Logger "rich>=14.0.0", # Used by JWK token auth handler @@ -58,7 +58,7 @@ dependencies = [ "jsonpath-ng>=1.6.1", "psycopg2-binary>=2.9.10", # LCORE-1565 - "litellm>=1.75.5.post1,<=1.82.6", + "litellm>=1.75.5.post1,<=1.84.0", "urllib3>=2.6.3", # Used for agent card configuration "PyYAML>=6.0.0", @@ -174,14 +174,14 @@ llslibdev = [ # API tool_runtime: remote::model-context-protocol "mcp>=1.23.0", # API post_training: inline::huggingface - "torch==2.9.0", + "torch==2.10.0", "trl>=0.18.2", "peft>=0.15.2", # API inference: remote::vertexai "google-cloud-aiplatform>=1.130.0", # API inference: remote::watsonx # LCORE-1565 - "litellm>=1.81.0,<=1.82.6", + "litellm>=1.81.0,<=1.84.0", # Other "autoevals>=0.0.129", "fire>=0.7.0", @@ -225,6 +225,9 @@ disable = ["R0801"] [tool.ruff] extend-exclude = ["tests/profiles/syntax_error.py"] +[tool.black] +target-version = ["py312"] + [tool.ruff.lint] extend-select = ["TID251", "UP006", "UP007", "UP010", "UP017", "UP035", "RUF100", "B009", "B010", "DTZ005", "D202", "I001"] diff --git a/requirements-build.txt b/requirements-build.txt deleted file mode 100644 index f586484d0..000000000 --- a/requirements-build.txt +++ /dev/null @@ -1,158 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.13 -# by the following command: -# -# pybuild-deps compile --output-file=requirements-build.txt requirements.source.txt -# -calver==2025.10.20 - # via trove-classifiers -cython==3.2.4 - # via oracledb -dunamai==1.26.0 - # via uv-dynamic-versioning -flit-core==3.12.0 - # via - # jinja2 - # packaging - # pathspec - # wheel -hatch-fancy-pypi-readme==25.1.0 - # via - # attrs - # llama-stack-client - # openai -hatch-vcs==0.5.0 - # via - # attrs - # chardet - # filelock -hatchling==1.26.3 - # via - # hatch-fancy-pypi-readme - # llama-stack-client - # openai -hatchling==1.29.0 - # via - # a2a-sdk - # attrs - # chardet - # filelock - # hatch-fancy-pypi-readme - # hatch-vcs - # opentelemetry-api - # opentelemetry-distro - # opentelemetry-exporter-otlp - # opentelemetry-exporter-otlp-proto-common - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http - # opentelemetry-instrumentation - # opentelemetry-proto - # opentelemetry-sdk - # opentelemetry-semantic-conventions - # pydantic-settings - # pythainlp - # starlette - # uv-dynamic-versioning - # uvicorn - # wcwidth -jinja2==3.1.6 - # via uv-dynamic-versioning -markupsafe==3.0.3 - # via jinja2 -maturin==1.10.2 - # via fastuuid -packaging==26.0 - # via - # hatchling - # setuptools-scm - # vcs-versioning - # wheel -pathspec==1.0.4 - # via hatchling -pdm-backend==2.4.7 - # via fastapi -pluggy==1.6.0 - # via hatchling -poetry-core==2.3.2 - # via - # dunamai - # litellm - # rich - # tomlkit -semantic-version==2.10.0 - # via setuptools-rust -setuptools-rust==1.12.1 - # via maturin -setuptools-scm==10.0.5 - # via - # hatch-vcs - # llama-stack - # llama-stack-api - # pluggy - # setuptools-rust - # tenacity -tomlkit==0.14.0 - # via uv-dynamic-versioning -trove-classifiers==2026.1.14.14 - # via hatchling -uv-dynamic-versioning==0.14.0 - # via a2a-sdk -vcs-versioning==1.1.1 - # via setuptools-scm -wheel==0.46.3 - # via - # authlib - # azure-core - # azure-identity - # cachetools - # litellm - # oci - # oracledb - # sentence-transformers - # tornado - -# The following packages are considered to be unsafe in a requirements file: -setuptools==82.0.0 - # via charset-normalizer -setuptools==82.0.1 - # via - # authlib - # azure-core - # azure-identity - # blobfile - # cachetools - # calver - # certifi - # emoji - # google-api-core - # google-cloud-bigquery - # googleapis-common-protos - # greenlet - # llama-stack - # llama-stack-api - # markupsafe - # maturin - # multiprocess - # oci - # oracledb - # pathspec - # pluggy - # polyleven - # proto-plus - # psutil - # pyasn1 - # pycryptodomex - # pyjwt - # python-dotenv - # regex - # requests - # semver - # sentence-transformers - # setuptools-rust - # setuptools-scm - # sse-starlette - # tenacity - # tornado - # trl - # trove-classifiers - # vcs-versioning diff --git a/requirements.hashes.source.txt b/requirements.hashes.source.txt deleted file mode 100644 index a5f160c68..000000000 --- a/requirements.hashes.source.txt +++ /dev/null @@ -1,902 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile requirements.source.txt --refresh --generate-hashes --python-version 3.12 --emit-index-url --no-deps --no-annotate ---index-url https://pypi.org/simple - -a2a-sdk==0.3.25 \ - --hash=sha256:2fce38faea82eb0b6f9f9c2bcf761b0d78612c80ef0e599b50d566db1b2654b5 \ - --hash=sha256:afda85bab8d6af0c5d15e82f326c94190f6be8a901ce562d045a338b7127242f -accelerate==1.13.0 \ - --hash=sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0 \ - --hash=sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236 -attrs==26.1.0 \ - --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ - --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 -authlib==1.6.9 \ - --hash=sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04 \ - --hash=sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3 -autoevals==0.1.0 \ - --hash=sha256:573ab490966fd5f2265dc4842d0bfd7b729ee121c86bd72db4440badb7264587 \ - --hash=sha256:ae884fe6107dbd6e05d840f51c2dba7eccfa01449e5ee5e83b6b4589508b2aca -azure-core==1.39.0 \ - --hash=sha256:4ac7b70fab5438c3f68770649a78daf97833caa83827f91df9c14e0e0ea7d34f \ - --hash=sha256:8a90a562998dd44ce84597590fff6249701b98c0e8797c95fcdd695b54c35d74 -azure-identity==1.25.3 \ - --hash=sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6 \ - --hash=sha256:f4d0b956a8146f30333e071374171f3cfa7bdb8073adb8c3814b65567aa7447c -blobfile==3.2.0 \ - --hash=sha256:78514a9265b9aa7d4607042dc77c5e6461ab27036450ad8e1f6ef9a7f29bf958 \ - --hash=sha256:e5e4095477da9f09e2077f41320c006001b2102a61f07d41ceaaecdf5d9741d8 -cachetools==7.0.5 \ - --hash=sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990 \ - --hash=sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114 -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 -chardet==7.3.0 \ - --hash=sha256:015f9d697f796a5bb0ac4d19e0108fd6cc84d141521089d0385d2a00c5f5d74e \ - --hash=sha256:04ef4b65c18afb298ac7c1326fbd83e1524250ceb7e61df67115a4a48e9e8e0e \ - --hash=sha256:23d264962f680deec691308cc753685b4c5a69798a26e00ce4f4cf96d2e43fcb \ - --hash=sha256:328a79efb197d6e9460ce33ebb8002a0bf85faa123d56aa1f75c21696ee70070 \ - --hash=sha256:340f4eccafa17cfc5a90af1fb8558dc791156f815f8d800d8ac2b1b9fb2b8103 \ - --hash=sha256:51a0ad2031bd10d24357d7e52de68abe29efb62b7aedf1bd9d1c593fd438cec5 \ - --hash=sha256:5276bc68736cf6d530ca2dc4ea1c2101d2b50015b09d48242615cc3818623574 \ - --hash=sha256:58388f412a76d1449c6ce4631edbd1b0087385ff00b6fb1f521879d8e8d99484 \ - --hash=sha256:5b58eb6d6dea67fc3b578dd956dd6e92aafea383aa559ccea55265554ff062a3 \ - --hash=sha256:5dbfbaef7de55aee008de9b886a63e60e916016507ed7845c9b7bfe1357f4f54 \ - --hash=sha256:5e912948f27c0124da8ce8fcd5ed8fe3b5e06fe8c0ecb66dd95180edf1122a6c \ - --hash=sha256:622206fdb0d274766559fa172e2d680d58cc5fc6a67015c21c36fec500e7bd50 \ - --hash=sha256:7193bcca96ffd87a7d6695056d3e875707b25eda588957756dd5c95d125fcb89 \ - --hash=sha256:7ccec08ee572dfd7a9486835ceba06ef88fa3e15deb1b76130b5ba6012fbb0c0 \ - --hash=sha256:7ed513fb0b35ebf663bd4f8be2d61af701202e98f77e6d8301d1a2c7d0f2985d \ - --hash=sha256:7f3758c122aebb411eac37452c5774c56a5500147e1c24af0d460cb0d748fb48 \ - --hash=sha256:80139a006a598e2db83fe6eaf7ac29c2a30cc5d10f255a89a9c6b9c04f81d67f \ - --hash=sha256:811029a7cf397ed32441aa134475f7047826fba496047a6afd7bba1da7fc4ab7 \ - --hash=sha256:8267de33ade8a496cb441eb86dd08c803043870a66b4f55aa44c02e1dbdbf93e \ - --hash=sha256:90a6249a455b8ecdb207a9f5a5eb46e1c21f2173eacb5e1603def61ed4784303 \ - --hash=sha256:920b91e9436c4d546862143313f48d4e6e0dd8510114ae0c5b194ff8c878a6c8 \ - --hash=sha256:963e6cac79e3a4fc51c8517b8df03b3467550dd00d53e775059dce6e2fc0d0ca \ - --hash=sha256:aab48b166f8aa8b086c93147ed6427376b8db2d783aff3f787c1c0819f6486bb \ - --hash=sha256:b69fd9685a7aada6e236a50bb93b51bd4fc08d29d1029492e64823a69c015f46 \ - --hash=sha256:c296044993dab62281176c07d0ca2b6e865044e38944d3f6e4ad661e222f4db8 \ - --hash=sha256:c88c464b5b65859984386c698b43ccfe88e96814f80433c2d5fdf40dc4b68bcb \ - --hash=sha256:dc4726af6ee00ee386293c34d86ba1fe18e0bd70a74612f1486da217d6a01249 \ - --hash=sha256:e464c2ec0be69ab6d5792c1acf354001b34e201efa2bee60ff4f7ded0e23f6a8 \ - --hash=sha256:e6bf602bb8a070524a19bac1cff2a10d62c71b09606f066251282870fa1466e5 \ - --hash=sha256:f4ea1f94f17fce29600e1661f773048ee9ed90a9300f1226018987f7d5961019 \ - --hash=sha256:fef009cdb37225b1f6259ddc18c3017cd1edddd85d57823f1c46c53503da41a1 \ - --hash=sha256:ff4d81f5561f2382343dc922e533c017d52bc55125e50551c18034fffcdbd0cb -charset-normalizer==3.4.6 \ - --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ - --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ - --hash=sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5 \ - --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ - --hash=sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f \ - --hash=sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0 \ - --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ - --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ - --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 \ - --hash=sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8 \ - --hash=sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264 \ - --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ - --hash=sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2 \ - --hash=sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4 \ - --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ - --hash=sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f \ - --hash=sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa \ - --hash=sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95 \ - --hash=sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab \ - --hash=sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297 \ - --hash=sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a \ - --hash=sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e \ - --hash=sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84 \ - --hash=sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8 \ - --hash=sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0 \ - --hash=sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9 \ - --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ - --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ - --hash=sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843 \ - --hash=sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565 \ - --hash=sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7 \ - --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ - --hash=sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b \ - --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ - --hash=sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687 \ - --hash=sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9 \ - --hash=sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14 \ - --hash=sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89 \ - --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ - --hash=sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0 \ - --hash=sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9 \ - --hash=sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a \ - --hash=sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389 \ - --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ - --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ - --hash=sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd \ - --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ - --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ - --hash=sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc \ - --hash=sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532 \ - --hash=sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d \ - --hash=sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae \ - --hash=sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2 \ - --hash=sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64 \ - --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ - --hash=sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557 \ - --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ - --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ - --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ - --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ - --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ - --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ - --hash=sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597 \ - --hash=sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c \ - --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ - --hash=sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2 \ - --hash=sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54 \ - --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ - --hash=sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4 \ - --hash=sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4 \ - --hash=sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7 \ - --hash=sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6 \ - --hash=sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5 \ - --hash=sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194 \ - --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ - --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ - --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ - --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ - --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ - --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ - --hash=sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923 \ - --hash=sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88 \ - --hash=sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f \ - --hash=sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21 \ - --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ - --hash=sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6 \ - --hash=sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc \ - --hash=sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2 \ - --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ - --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ - --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ - --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ - --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ - --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ - --hash=sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237 \ - --hash=sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4 \ - --hash=sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778 \ - --hash=sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb \ - --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ - --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ - --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ - --hash=sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f \ - --hash=sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5 \ - --hash=sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611 \ - --hash=sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8 \ - --hash=sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf \ - --hash=sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d \ - --hash=sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b \ - --hash=sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db \ - --hash=sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e \ - --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ - --hash=sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd \ - --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ - --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ - --hash=sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8 \ - --hash=sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe \ - --hash=sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058 \ - --hash=sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17 \ - --hash=sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833 \ - --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ - --hash=sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550 \ - --hash=sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff \ - --hash=sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2 \ - --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ - --hash=sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982 \ - --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ - --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed \ - --hash=sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104 \ - --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 -circuitbreaker==2.1.3 \ - --hash=sha256:1a4baee510f7bea3c91b194dcce7c07805fe96c4423ed5594b75af438531d084 \ - --hash=sha256:87ba6a3ed03fdc7032bc175561c2b04d52ade9d5faf94ca2b035fbdc5e6b1dd1 -emoji==2.15.0 \ - --hash=sha256:205296793d66a89d88af4688fa57fd6496732eb48917a87175a023c8138995eb \ - --hash=sha256:eae4ab7d86456a70a00a985125a03263a5eac54cd55e51d7e184b1ed3b6757e4 -fastapi==0.135.2 \ - --hash=sha256:0af0447d541867e8db2a6a25c23a8c4bd80e2394ac5529bd87501bbb9e240ca5 \ - --hash=sha256:88a832095359755527b7f63bb4c6bc9edb8329a026189eed83d6c1afcf419d56 -fastuuid==0.14.0 \ - --hash=sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1 \ - --hash=sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede \ - --hash=sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11 \ - --hash=sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995 \ - --hash=sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc \ - --hash=sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796 \ - --hash=sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed \ - --hash=sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7 \ - --hash=sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab \ - --hash=sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b \ - --hash=sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00 \ - --hash=sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26 \ - --hash=sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4 \ - --hash=sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219 \ - --hash=sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75 \ - --hash=sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714 \ - --hash=sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b \ - --hash=sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94 \ - --hash=sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36 \ - --hash=sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346 \ - --hash=sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4 \ - --hash=sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8 \ - --hash=sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3 \ - --hash=sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87 \ - --hash=sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4 \ - --hash=sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8 \ - --hash=sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3 \ - --hash=sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea \ - --hash=sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6 \ - --hash=sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722 \ - --hash=sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a \ - --hash=sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0 \ - --hash=sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85 \ - --hash=sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34 \ - --hash=sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021 \ - --hash=sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a \ - --hash=sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d \ - --hash=sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a \ - --hash=sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09 \ - --hash=sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8 \ - --hash=sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c \ - --hash=sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176 \ - --hash=sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4 \ - --hash=sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc \ - --hash=sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad \ - --hash=sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24 \ - --hash=sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f \ - --hash=sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f \ - --hash=sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f \ - --hash=sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741 \ - --hash=sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5 \ - --hash=sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4 \ - --hash=sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209 \ - --hash=sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470 \ - --hash=sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad \ - --hash=sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057 \ - --hash=sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8 \ - --hash=sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe \ - --hash=sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73 \ - --hash=sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836 \ - --hash=sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8 \ - --hash=sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779 \ - --hash=sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b \ - --hash=sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d \ - --hash=sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022 \ - --hash=sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7 \ - --hash=sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070 \ - --hash=sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105 \ - --hash=sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173 \ - --hash=sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397 \ - --hash=sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505 \ - --hash=sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a \ - --hash=sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06 \ - --hash=sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa \ - --hash=sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06 \ - --hash=sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8 \ - --hash=sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad \ - --hash=sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 -google-api-core==2.30.0 \ - --hash=sha256:02edfa9fab31e17fc0befb5f161b3bf93c9096d99aed584625f38065c511ad9b \ - --hash=sha256:80be49ee937ff9aba0fd79a6eddfde35fe658b9953ab9b79c57dd7061afa8df5 -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 -google-cloud-aiplatform==1.143.0 \ - --hash=sha256:1f0124a89795a6b473deb28724dd37d95334205df3a9c9c48d0b8d7a3d5d5cc4 \ - --hash=sha256:78df97d044859f743a9cc48b89a260d33579b0d548b1589bb3ae9f4c2afc0c5a -google-cloud-bigquery==3.40.1 \ - --hash=sha256:75afcfb6e007238fe1deefb2182105249321145ff921784fe7b1de2b4ba24506 \ - --hash=sha256:9082a6b8193aba87bed6a2c79cf1152b524c99bb7e7ac33a785e333c09eac868 -google-cloud-resource-manager==1.17.0 \ - --hash=sha256:0f486b62e2c58ff992a3a50fa0f4a96eef7750aa6c971bb373398ccb91828660 \ - --hash=sha256:e479baf4b014a57f298e01b8279e3290b032e3476d69c8e5e1427af8f82739a5 -google-cloud-storage==3.10.1 \ - --hash=sha256:97db9aa4460727982040edd2bd13ff3d5e2260b5331ad22895802da1fc2a5286 \ - --hash=sha256:a72f656759b7b99bda700f901adcb3425a828d4a29f911bc26b3ea79c5b1217f -googleapis-common-protos==1.73.1 \ - --hash=sha256:13114f0e9d2391756a0194c3a8131974ed7bffb06086569ba193364af59163b6 \ - --hash=sha256:e51f09eb0a43a8602f5a915870972e6b4a394088415c79d79605a46d8e826ee8 -greenlet==3.3.2 \ - --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ - --hash=sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082 \ - --hash=sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b \ - --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ - --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ - --hash=sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727 \ - --hash=sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e \ - --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ - --hash=sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f \ - --hash=sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327 \ - --hash=sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd \ - --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ - --hash=sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070 \ - --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ - --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ - --hash=sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79 \ - --hash=sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7 \ - --hash=sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e \ - --hash=sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf \ - --hash=sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f \ - --hash=sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506 \ - --hash=sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a \ - --hash=sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395 \ - --hash=sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4 \ - --hash=sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca \ - --hash=sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492 \ - --hash=sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab \ - --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ - --hash=sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce \ - --hash=sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5 \ - --hash=sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef \ - --hash=sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d \ - --hash=sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac \ - --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ - --hash=sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124 \ - --hash=sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4 \ - --hash=sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986 \ - --hash=sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd \ - --hash=sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f \ - --hash=sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb \ - --hash=sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4 \ - --hash=sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13 \ - --hash=sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab \ - --hash=sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff \ - --hash=sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a \ - --hash=sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9 \ - --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 \ - --hash=sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd \ - --hash=sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71 \ - --hash=sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92 \ - --hash=sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643 \ - --hash=sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54 \ - --hash=sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9 -huggingface-hub==0.36.2 \ - --hash=sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a \ - --hash=sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270 -jsonpath-ng==1.8.0 \ - --hash=sha256:54252968134b5e549ea5b872f1df1168bd7defe1a52fed5a358c194e1943ddc3 \ - --hash=sha256:b8dde192f8af58d646fc031fac9c99fe4d00326afc4148f1f043c601a8cfe138 -langdetect==1.0.9 \ - --hash=sha256:7cbc0746252f19e76f77c0b1690aadf01963be835ef0cd4b56dddf2a8f1dfc2a \ - --hash=sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0 -litellm==1.82.6 \ - --hash=sha256:164a3ef3e19f309e3cabc199bef3d2045212712fefdfa25fc7f75884a5b5b205 \ - --hash=sha256:2aa1c2da21fe940c33613aa447119674a3ad4d2ad5eb064e4d5ce5ee42420136 -llama-stack==0.5.2 \ - --hash=sha256:581fda638088ee029aab20afe3c42ba8f7f6ef21c80bd9ebcae20bb13c3409d3 \ - --hash=sha256:9334c781e4ded6520aa60c3301a9087e9fb8fdaea8e5f30f8e21d85b17231d8d -llama-stack-api==0.5.2 \ - --hash=sha256:6531556dd8bb6555d778360ecfcd850aad7a49a8172b68146995d538e71641f0 \ - --hash=sha256:a272e4b803fe24a8ba7d22e6d904bf88abd118ba0b6610a20ff5dedb09f38ad7 -llama-stack-client==0.5.2 \ - --hash=sha256:17c1bbad90f7699da4eb3cae256e8823caa4d2be945512a45c8c6f89ab899f28 \ - --hash=sha256:473f4d67ac0b243b0fc29555a0203a742615d31bea606b4332d9e2f193f73d6a -markupsafe==3.0.3 \ - --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ - --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ - --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ - --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ - --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ - --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ - --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ - --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ - --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ - --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ - --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ - --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ - --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ - --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ - --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ - --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ - --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ - --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ - --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ - --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ - --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ - --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ - --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ - --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ - --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ - --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ - --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ - --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ - --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ - --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ - --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ - --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ - --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ - --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ - --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ - --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ - --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ - --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ - --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ - --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ - --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ - --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ - --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ - --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ - --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ - --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ - --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ - --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ - --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ - --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ - --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ - --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ - --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ - --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ - --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ - --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ - --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ - --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ - --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ - --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ - --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ - --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ - --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ - --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ - --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ - --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ - --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ - --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ - --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ - --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ - --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ - --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ - --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ - --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ - --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ - --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ - --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ - --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ - --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ - --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ - --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ - --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ - --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ - --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ - --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ - --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ - --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ - --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ - --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 -msal==1.35.1 \ - --hash=sha256:70cac18ab80a053bff86219ba64cfe3da1f307c74b009e2da57ef040eb1b5656 \ - --hash=sha256:8f4e82f34b10c19e326ec69f44dc6b30171f2f7098f3720ea8a9f0c11832caa3 -msal-extensions==1.3.1 \ - --hash=sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca \ - --hash=sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4 -multiprocess==0.70.18 \ - --hash=sha256:06b19433de0d02afe5869aec8931dd5c01d99074664f806c73896b0d9e527213 \ - --hash=sha256:0929ba95831adb938edbd5fb801ac45e705ecad9d100b3e653946b7716cb6bd3 \ - --hash=sha256:25d4012dcaaf66b9e8e955f58482b42910c2ee526d532844d8bcf661bbc604df \ - --hash=sha256:2dbaae9bffa1fb2d58077c0044ffe87a8c8974e90fcf778cdf90e139c970d42a \ - --hash=sha256:3fbba48bfcd932747c33f0b152b26207c4e0840c35cab359afaff7a8672b1031 \ - --hash=sha256:4d77f8e4bfe6c6e2e661925bbf9aed4d5ade9a1c6502d5dfc10129b9d1141797 \ - --hash=sha256:5aa6eef98e691281b3ad923be2832bf1c55dd2c859acd73e5ec53a66aae06a1d \ - --hash=sha256:5f9be0342e597dde86152c10442c5fb6c07994b1c29de441b7a3a08b0e6be2a0 \ - --hash=sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea \ - --hash=sha256:6fa1366f994373aaf2d4738b0f56e707caeaa05486e97a7f71ee0853823180c2 \ - --hash=sha256:871743755f43ef57d7910a38433cfe41319e72be1bbd90b79c7a5ac523eb9334 \ - --hash=sha256:8b8940ae30139e04b076da6c5b83e9398585ebdf0f2ad3250673fef5b2ff06d6 \ - --hash=sha256:9b78f8e5024b573730bfb654783a13800c2c0f2dfc0c25e70b40d184d64adaa2 \ - --hash=sha256:9fd8d662f7524a95a1be7cbea271f0b33089fe792baabec17d93103d368907da \ - --hash=sha256:bcac5a4e81f1554d98d1bba963eeb1bd24966432f04fcbd29b6e1a16251ad712 \ - --hash=sha256:c0c7cd75d0987ab6166d64e654787c781dbacbcbcaaede4c1ffe664720b3e14b \ - --hash=sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b \ - --hash=sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8 \ - --hash=sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d -nltk==3.9.4 \ - --hash=sha256:ed03bc098a40481310320808b2db712d95d13ca65b27372f8a403949c8b523d0 \ - --hash=sha256:f2fa301c3a12718ce4a0e9305c5675299da5ad9e26068218b69d692fda84828f -oci==2.168.3 \ - --hash=sha256:098c7729a5e97e2bbd67dfb2218da355fbaff79a3e1c20cdbf137c79e97e1e1c \ - --hash=sha256:370294488351dbb7cddc208bbbf64535e2b1f065e61f7f893de0ba1ac61c2d9a -openai==2.30.0 \ - --hash=sha256:92f7661c990bda4b22a941806c83eabe4896c3094465030dd882a71abe80c885 \ - --hash=sha256:9a5ae616888eb2748ec5e0c5b955a51592e0b201a11f4262db920f2a78c5231d -opentelemetry-api==1.40.0 \ - --hash=sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f \ - --hash=sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9 -opentelemetry-distro==0.61b0 \ - --hash=sha256:975b845f50181ad53753becf4fd4b123b54fa04df5a9d78812264436d6518981 \ - --hash=sha256:f21d1ac0627549795d75e332006dd068877f00e461b1b2e8fe4568d6eb7b9590 -opentelemetry-exporter-otlp==1.40.0 \ - --hash=sha256:48c87e539ec9afb30dc443775a1334cc5487de2f72a770a4c00b1610bf6c697d \ - --hash=sha256:7caa0870b95e2fcb59d64e16e2b639ecffb07771b6cd0000b5d12e5e4fef765a -opentelemetry-exporter-otlp-proto-common==1.40.0 \ - --hash=sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa \ - --hash=sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149 -opentelemetry-exporter-otlp-proto-grpc==1.40.0 \ - --hash=sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52 \ - --hash=sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740 -opentelemetry-exporter-otlp-proto-http==1.40.0 \ - --hash=sha256:a8d1dab28f504c5d96577d6509f80a8150e44e8f45f82cdbe0e34c99ab040069 \ - --hash=sha256:db48f5e0f33217588bbc00274a31517ba830da576e59503507c839b38fa0869c -opentelemetry-instrumentation==0.61b0 \ - --hash=sha256:92a93a280e69788e8f88391247cc530fd81f16f2b011979d4d6398f805cfbc63 \ - --hash=sha256:cb21b48db738c9de196eba6b805b4ff9de3b7f187e4bbf9a466fa170514f1fc7 -opentelemetry-proto==1.40.0 \ - --hash=sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd \ - --hash=sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f -opentelemetry-sdk==1.40.0 \ - --hash=sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2 \ - --hash=sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1 -opentelemetry-semantic-conventions==0.61b0 \ - --hash=sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a \ - --hash=sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2 -oracledb==3.4.2 \ - --hash=sha256:00c79448017f367bb7ab6900efe0706658a53768abea2b4519a4c9b2d5743890 \ - --hash=sha256:0e16fe3d057e0c41a23ad2ae95bfa002401690773376d476be608f79ac74bf05 \ - --hash=sha256:0f04a2d62073407672f114d02529921de0677c6883ed7c64d8d1a3c04caa3238 \ - --hash=sha256:1617a1db020346883455af005efbefd51be2c4d797e43b1b38455a19f8526b48 \ - --hash=sha256:19fa80ef84f85ad74077aa626067bbe697e527bd39604b4209f9d86cb2876b89 \ - --hash=sha256:1e4930d7f6584832dcc15b8ca415a7957b0c45f5aa7c4f88702e070e5c53bf93 \ - --hash=sha256:23aa07c1eaca17ae74c6fdc86b218f58484d56452958aead1aa460c0596a76c1 \ - --hash=sha256:31b7ee83c23d0439778303de8a675717f805f7e8edb5556d48c4d8343bcf14f5 \ - --hash=sha256:3df8eee1410d25360599968b1625b000f10c5ae0e47274031a7842a9dc418890 \ - --hash=sha256:404ec1451d0448653ee074213b87d6c5bd65eaa74b50083ddf2c9c3e11c71c71 \ - --hash=sha256:46e0f2278ff1fe83fbc33a3b93c72d429323ec7eed47bc9484e217776cd437e5 \ - --hash=sha256:55397e7eb43bb7017c03a981c736c25724182f5210951181dfe3fab0e5d457fb \ - --hash=sha256:574c8280d49cbbe21dbe03fc28356d9b9a5b9e300ebcde6c6d106e51453a7e65 \ - --hash=sha256:59ad6438f56a25e8e1a4a3dd1b42235a5d09ab9ba417ff2ad14eae6596f3d06f \ - --hash=sha256:5d7befb014174c5ae11c3a08f5ed6668a25ab2335d8e7104dca70d54d54a5b3a \ - --hash=sha256:5ed78d7e7079a778062744ccf42141ce4806818c3f4dd6463e4a7edd561c9f86 \ - --hash=sha256:643c25d301a289a371e37fcedb59e5fa5e54fb321708e5c12821c4b55bdd8a4d \ - --hash=sha256:6d85622664cc88d5a82bbd7beccb62cd53bd272c550a5e15e7d5f8ae6b86f1f1 \ - --hash=sha256:9f434a739405557bd57cb39b62238142bb27855a524a70dc6d397a2a8c576c9d \ - --hash=sha256:a7396664e592881225ba66385ee83ce339d864f39003d6e4ca31a894a7e7c552 \ - --hash=sha256:ac25a0448fc830fb7029ad50cd136cdbfcd06975d53967e269772cc5cb8c203a \ - --hash=sha256:b1095d95d0c8b37e4d0e17cf1928919cb59222b6344362a1cf6a2f3ca205a28a \ - --hash=sha256:b26a10f9c790bd141ffc8af68520803ed4a44a9258bf7d1eea9bfdd36bd6df7f \ - --hash=sha256:b8e4b8a852251cef09038b75f30fce1227010835f4e19cfbd436027acba2697c \ - --hash=sha256:b974caec2c330c22bbe765705a5ac7d98ec3022811dec2042d561a3c65cb991b \ - --hash=sha256:d7ce75c498bff758548ec6e4424ab4271aa257e5887cc436a54bc947fd46199a \ - --hash=sha256:d8d75e4f879b908be66cce05ba6c05791a5dbb4a15e39abc01aa25c8a2492bd9 \ - --hash=sha256:e068ef844a327877bfefbef1bc6fb7284c727bb87af80095f08d95bcaf7b8bb2 \ - --hash=sha256:f8ea989965a4f636a309444bd696ab877bba373d5d67bf744785f9bd8c560865 \ - --hash=sha256:f93cae08e8ed20f2d5b777a8602a71f9418389c661d2c937e84d94863e7e7011 \ - --hash=sha256:ff3c89cecea62af8ca02aa33cab0f2edc0214c747eac7d3364ed6b2640cb55e4 -polyleven==0.11.0 \ - --hash=sha256:046e90c02c5b8dae2ab71c4fb33772bd6f27b7883b05e2117573bf478b5ced44 \ - --hash=sha256:05207bb66da15a2dc5c530e2f5cb5f0588d0a7e79b3bd542965f9e06e3fb14fe \ - --hash=sha256:0cb8ed97b536f9aada3ad45169ee7768c426498bf3fa608a4eabd055dfef795e \ - --hash=sha256:0edbc74858bcd41e02865ad9431f8c78752161d96ab61d3643dcfc3f334bed58 \ - --hash=sha256:14601c9ea632d0d4bd95e78f4ca0752d1de26f75aa3d983ad49800f07f76f90b \ - --hash=sha256:166f6c9b161c6af92ff201c734d6437bc7ef74a32dab306c5d47a0bdb7a82d9f \ - --hash=sha256:16986fd58911d6075b5f63ea001197141145b7a6df48bc4ce4530e79227e74a2 \ - --hash=sha256:209fa669ca23ac453a7e9fbf07695350d5cbe61d71a6226b861757ccab28e664 \ - --hash=sha256:248b9f645d8c6e337091498ed5c7d4a796d9d51df98458be25b1d76d962954e2 \ - --hash=sha256:2a33728df4708c9370f5e65fdb8de7e15f01d5ae8530eedf507d182fe63afb0e \ - --hash=sha256:2a59849c327279902e8b396666f6998234aa82aacc47abc103d93babaad46203 \ - --hash=sha256:2eb8f6778f3073dce041805a09f0753cc441b0219253d7b933aad234a954f30a \ - --hash=sha256:2f975ab8cb81fd8eb5a647a3cefb0bb80bc307920a9307f66ab4019d88370ed2 \ - --hash=sha256:310caad59e5cb02553806ba5a66d0cbc533e17d17e6dc90dcc810f20f230e183 \ - --hash=sha256:367faf0e1898c79624f46a894ebe5b69bf1782318ec3e3331676ce5b24352882 \ - --hash=sha256:3bfce4689b6aaacf7c5296b8ed11ada07ccf046a01097ba1681e10f9caabbf6f \ - --hash=sha256:3c18b8e44e5d04f1ffa7d41eb68da553833ab8663b7cfb1a505d85676db5c797 \ - --hash=sha256:3c93419c84e5cabfcf38cdaf322c915c58866ce1828a3cfaebc06bbe2fc66d6d \ - --hash=sha256:45487a1e4a8415e4ed45e6720b2a3ad9d240336f7afa136a625b8f802a1880c2 \ - --hash=sha256:45cfb234fece0c9df73276788fa529a25f91abf97dd0d9aed4f1b713b6d530e3 \ - --hash=sha256:4648732c8ad3955c8d7b1aa015d92936a150475aaa97ce704fe0c8e7fa7e0c4f \ - --hash=sha256:47a3fb5b8cb60f647d2832d38b7d87cda27da8622b27c1292bceb9a04954c189 \ - --hash=sha256:4b8e5ac8faecc6daa7b3d325436a3f23f8c33dec7bfca5d22df3fbe00f92ddd9 \ - --hash=sha256:4c78b4d3e7d7b74315d5422178118963374c0cf3d7a9532a955f446ed365320a \ - --hash=sha256:50bb7d68b790194d552ee1256a02e205486b27eb22ab333eeb0003e0271c4846 \ - --hash=sha256:561f028c9535223c78cd58f6b546dd15ce69a6e268e651ae1377644845fae639 \ - --hash=sha256:5808f62874187dfd4e30de5dd5f42a660562ec95a87cc64d5455ba0f4be8f175 \ - --hash=sha256:58c11ce44466f6d833fd90f77ccd0c44accce41c9e80dea4c2817d5c124a61d5 \ - --hash=sha256:5c9ccb2f327d49a7566b0192e0d426f7772b38e247dc4e809c0b1cdf23e2ecc2 \ - --hash=sha256:6526d2516b439065864722069de6fcc418a4135696990dad66b81ddb18863bd9 \ - --hash=sha256:65fc01fe6cfe287f2f20170b35687a436ab36b882db568a55d81d6e0acd8379d \ - --hash=sha256:6ba2dcf3aff2909bbf3bdd9c1749f8de207f023fbb2c0b1d681c6bf3e78ceef1 \ - --hash=sha256:71bbb17919548d4e162444c918b1acf864f84150197087c757975606cbb99e43 \ - --hash=sha256:71d7c324e13ace16ecb7f1ba4d37e64dd34419187c6827f62b419421646ff73a \ - --hash=sha256:758c5fcb9d8556720fb51c1de5f3a7b39bb8dce9510a1f40e5f287951b901010 \ - --hash=sha256:7859ce33fe4184e52da6c2161f806e5c26faefff148ba16f7205c8d779e2ed68 \ - --hash=sha256:7ab547adc0ac72a2852d37337a4a839d4e2f713940b0e8a944d45c528e5e6538 \ - --hash=sha256:83e59c8590a06ea6a959a3c55e6d28544b8d11a51aac2a318c1b74f92575dd28 \ - --hash=sha256:864aed924fd22bac26b6afefdb7455132a98922c060a20aee317b1a2a1c394d1 \ - --hash=sha256:8742d8bc9225237af5b574e2f8b95757418c6329f2d6bfdf3eb1d2bc44241880 \ - --hash=sha256:88a35ec93ec3d81a7347fd49db314a914798a144dca3d22946d18bba9b597dec \ - --hash=sha256:909d76c5e28d555b4174677041c0909f540f4948b7f77a86739e3dda6b34a9e0 \ - --hash=sha256:94311ee39e2db957415eacb36b96ae26dcc427c260465324de45fb8c870d4661 \ - --hash=sha256:94832ff5d04022ba6038c2ca0c9ea6906330cde3a3b1761739d772647d01da33 \ - --hash=sha256:9aaed455f498172769fd88f83c27bb8f43e0583d7b27d6b343154d471ec2145e \ - --hash=sha256:9b97b9260730deda4cdd5878fd6ce128b970497da0fbefeeacc0b1ed4c59ebb7 \ - --hash=sha256:9d34f655ae577fd91c6fa01e86e0396bf9700ffb8f9ef936306b6f14bd061d98 \ - --hash=sha256:9deb75346b4177d5e69496791e6156f705d9059961ce8f9520a0dc96532f10f2 \ - --hash=sha256:9e0d77012697d5a3ad74f0c8bacdade26ec38d99ce40f537cca3f44cf3a94af9 \ - --hash=sha256:a11326921f1581e72072e921eb572049945529a856d1c6a981c6fd6316f298d3 \ - --hash=sha256:a28860fe33a7f907bc5f86e55a0b9faea80047d1677fa23b4d6c631ccf91ef2f \ - --hash=sha256:a472fccba89ffb44b10481760c7351c79855b0ff654ec9d28966bfb111a71748 \ - --hash=sha256:b2aada9dd04e84389d90790f359447447a499d6d86807697d80732ed45547a43 \ - --hash=sha256:bb8c41d845a7c11fd33d2f4c9287204777943e7ba0da4ab772ef19926a54f5f2 \ - --hash=sha256:bf47079b6dc62e6af2bd6ecb45a6087efd9a27b61666b98d0326c246a22ea991 \ - --hash=sha256:bf82bb8601582da8f2248293c1e6f4cce2025c79fd64fccddf67dd8538655b55 \ - --hash=sha256:c1a02e3f0acfd1164cbaea25192398bc943ee9b93b9883a1fba9b2613d3616b0 \ - --hash=sha256:c518ced3e7c05de4efbd12fd7b61d6d574eb170f431e0415689d9f143fe552ee \ - --hash=sha256:c6a814629cc0468f9800b1333414a3be08fda9c5ce6b63e97154a9d21732e590 \ - --hash=sha256:cae70197d545a09bfab8d7e506eed66ef314fa6c4e7a5e2c402c2febc31db74b \ - --hash=sha256:cb23352025d8f1eab1f71548a8a1579e554ed7cd82098d6a4f7752c8dc8b28c0 \ - --hash=sha256:ccf87f6ac8d76aa4c48a4828becc0c19fd1589b14b20affe23e5e012be4fa64f \ - --hash=sha256:ce264f6a9daa3265299d8ffcb180d8256517a8d9235613a3b267172da0bc1e06 \ - --hash=sha256:ce2e782f8fae812c7ad960c4fd17a58ada183b89ae220cacfe6b3234179872f4 \ - --hash=sha256:d74d348387cf340051711c0dd6af993b4c264daa78470098de16f4a2b725785c \ - --hash=sha256:dc4f17007b07fde292ad33ec43a3ae8febe27a5bd92462b920736fd81d774fce \ - --hash=sha256:e6182ea6142904ea50cf82e2955d922156b5fcf9a8279925f312961f16710a58 \ - --hash=sha256:e7b6c8cfa13114bc2b17b51503a4db0cbc358c3c96197d6d7283bd686c0fd8fb \ - --hash=sha256:e9fd720e67c2385b5a8ee970d01343d6e249dd03b11c2fea0a933a67dbce63f8 \ - --hash=sha256:ec83e88b28a77d3be353e13840893cc57a7d9272a07c7dac169de3578c0d2025 \ - --hash=sha256:ef28c4c6cdc71a32f0478772d2f07b2cd412fe7950182033b1c36c8a481b0834 \ - --hash=sha256:fa49732cdecd985241db9f78d5fdba7170ba6375d2bf9ad040b05127dc96b877 -proto-plus==1.27.2 \ - --hash=sha256:6432f75893d3b9e70b9c412f1d2f03f65b11fb164b793d14ae2ca01821d22718 \ - --hash=sha256:b2adde53adadf75737c44d3dcb0104fde65250dfc83ad59168b4aa3e574b6a24 -protobuf==6.33.6 \ - --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \ - --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \ - --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \ - --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \ - --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \ - --hash=sha256:bd56799fb262994b2c2faa1799693c95cc2e22c62f56fb43af311cae45d26f0e \ - --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \ - --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \ - --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593 \ - --hash=sha256:f443a394af5ed23672bc6c486be138628fbe5c651ccbc536873d7da23d1868cf -psutil==7.2.2 \ - --hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \ - --hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \ - --hash=sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841 \ - --hash=sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63 \ - --hash=sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979 \ - --hash=sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a \ - --hash=sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b \ - --hash=sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9 \ - --hash=sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee \ - --hash=sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312 \ - --hash=sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b \ - --hash=sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9 \ - --hash=sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e \ - --hash=sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc \ - --hash=sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1 \ - --hash=sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf \ - --hash=sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea \ - --hash=sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988 \ - --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ - --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ - --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 -pyaml==26.2.1 \ - --hash=sha256:489dd82997235d4cfcf76a6287fce2f075487d77a6567c271e8d790583690c68 \ - --hash=sha256:6261c2f0a2f33245286c794ad6ec234be33a73d2b05427079fd343e2812a87cf -pyasn1==0.6.3 \ - --hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \ - --hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde -pycryptodomex==3.23.0 \ - --hash=sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51 \ - --hash=sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6 \ - --hash=sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe \ - --hash=sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c \ - --hash=sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541 \ - --hash=sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e \ - --hash=sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010 \ - --hash=sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245 \ - --hash=sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587 \ - --hash=sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648 \ - --hash=sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff \ - --hash=sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f \ - --hash=sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9 \ - --hash=sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd \ - --hash=sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462 \ - --hash=sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa \ - --hash=sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003 \ - --hash=sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da \ - --hash=sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886 \ - --hash=sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785 \ - --hash=sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744 \ - --hash=sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d \ - --hash=sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8 \ - --hash=sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328 \ - --hash=sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7 \ - --hash=sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545 \ - --hash=sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708 \ - --hash=sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c \ - --hash=sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9 \ - --hash=sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314 \ - --hash=sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5 \ - --hash=sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66 \ - --hash=sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4 \ - --hash=sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08 \ - --hash=sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006 \ - --hash=sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a \ - --hash=sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5 \ - --hash=sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798 \ - --hash=sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c \ - --hash=sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea \ - --hash=sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 -pyjwt==2.12.1 \ - --hash=sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c \ - --hash=sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b -pyopenssl==25.3.0 \ - --hash=sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6 \ - --hash=sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329 -pythainlp==5.3.3 \ - --hash=sha256:b223291af6dba4882702272812e5927fc2eb2848cd657c8c2a2a13ca50ed6161 \ - --hash=sha256:c7e5d43238ac882479dd3f303e76838255ba7358da77553ed2a1296395ffe7b5 -python-dotenv==1.2.2 \ - --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ - --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 -pytz==2026.1.post1 \ - --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ - --hash=sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a -regex==2026.3.32 \ - --hash=sha256:03c2ebd15ff51e7b13bb3dc28dd5ac18cd39e59ebb40430b14ae1a19e833cff1 \ - --hash=sha256:09e26cad1544d856da85881ad292797289e4406338afe98163f3db9f7fac816c \ - --hash=sha256:0cec365d44835b043d7b3266487797639d07d621bec9dc0ea224b00775797cc1 \ - --hash=sha256:0d7855f5e59fcf91d0c9f4a51dc5d8847813832a2230c3e8e35912ccf20baaa2 \ - --hash=sha256:0f21ae18dfd15752cdd98d03cbd7a3640be826bfd58482a93f730dbd24d7b9fb \ - --hash=sha256:10fb2aaae1aaadf7d43c9f3c2450404253697bf8b9ce360bd5418d1d16292298 \ - --hash=sha256:110ba4920721374d16c4c8ea7ce27b09546d43e16aea1d7f43681b5b8f80ba61 \ - --hash=sha256:12917c6c6813ffcdfb11680a04e4d63c5532b88cf089f844721c5f41f41a63ad \ - --hash=sha256:18eb45f711e942c27dbed4109830bd070d8d618e008d0db39705f3f57070a4c6 \ - --hash=sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9 \ - --hash=sha256:1ca02ff0ef33e9d8276a1fcd6d90ff6ea055a32c9149c0050b5b67e26c6d2c51 \ - --hash=sha256:1cb22fa9ee6a0acb22fc9aecce5f9995fe4d2426ed849357d499d62608fbd7f9 \ - --hash=sha256:1e0f6648fd48f4c73d801c55ab976cd602e2da87de99c07bff005b131f269c6a \ - --hash=sha256:245667ad430745bae6a1e41081872d25819d86fbd9e0eec485ba00d9f78ad43d \ - --hash=sha256:2820d2231885e97aff0fcf230a19ebd5d2b5b8a1ba338c20deb34f16db1c7897 \ - --hash=sha256:2c8d402ea3dfe674288fe3962016affd33b5b27213d2b5db1823ffa4de524c57 \ - --hash=sha256:2dcca2bceb823c9cc610e57b86a265d7ffc30e9fe98548c609eba8bd3c0c2488 \ - --hash=sha256:2ffbadc647325dd4e3118269bda93ded1eb5f5b0c3b7ba79a3da9fbd04f248e9 \ - --hash=sha256:34c905a721ddee0f84c99e3e3b59dd4a5564a6fe338222bc89dd4d4df166115c \ - --hash=sha256:3c054e39a9f85a3d76c62a1d50c626c5e9306964eaa675c53f61ff7ec1204bbb \ - --hash=sha256:3c0bbfbd38506e1ea96a85da6782577f06239cb9fcf9696f1ea537c980c0680b \ - --hash=sha256:3e221b615f83b15887636fcb90ed21f1a19541366f8b7ba14ba1ad8304f4ded4 \ - --hash=sha256:3ea568832eca219c2be1721afa073c1c9eb8f98a9733fdedd0a9747639fc22a5 \ - --hash=sha256:3f5747501b69299c6b0b047853771e4ed390510bada68cb16da9c9c2078343f7 \ - --hash=sha256:462a041d2160090553572f6bb0be417ab9bb912a08de54cb692829c871ee88c1 \ - --hash=sha256:4bc32b4dbdb4f9f300cf9f38f8ea2ce9511a068ffaa45ac1373ee7a943f1d810 \ - --hash=sha256:4d082be64e51671dd5ee1c208c92da2ddda0f2f20d8ef387e57634f7e97b6aae \ - --hash=sha256:4f9ae4755fa90f1dc2d0d393d572ebc134c0fe30fcfc0ab7e67c1db15f192041 \ - --hash=sha256:51a93452034d671b0e21b883d48ea66c5d6a05620ee16a9d3f229e828568f3f0 \ - --hash=sha256:51fb7e26f91f9091fd8ec6a946f99b15d3bc3667cb5ddc73dd6cb2222dd4a1cc \ - --hash=sha256:5336b1506142eb0f23c96fb4a34b37c4fefd4fed2a7042069f3c8058efe17855 \ - --hash=sha256:567b57eb987547a23306444e4f6f85d4314f83e65c71d320d898aa7550550443 \ - --hash=sha256:5aa78c857c1731bdd9863923ffadc816d823edf475c7db6d230c28b53b7bdb5e \ - --hash=sha256:5bf2f3c2c5bd8360d335c7dcd4a9006cf1dabae063ee2558ee1b07bbc8a20d88 \ - --hash=sha256:5c35d097f509cf7e40d20d5bee548d35d6049b36eb9965e8d43e4659923405b9 \ - --hash=sha256:5d86e3fb08c94f084a625c8dc2132a79a3a111c8bf6e2bc59351fa61753c2f6e \ - --hash=sha256:6062c4ef581a3e9e503dccf4e1b7f2d33fdc1c13ad510b287741ac73bc4c6b27 \ - --hash=sha256:6128dd0793a87287ea1d8bf16b4250dd96316c464ee15953d5b98875a284d41e \ - --hash=sha256:631f7d95c83f42bccfe18946a38ad27ff6b6717fb4807e60cf24860b5eb277fc \ - --hash=sha256:66a5083c3ffe5a5a95f8281ea47a88072d4f24001d562d1d9d28d4cdc005fec5 \ - --hash=sha256:66d3126afe7eac41759cd5f0b3b246598086e88e70527c0d68c9e615b81771c4 \ - --hash=sha256:67015a8162d413af9e3309d9a24e385816666fbf09e48e3ec43342c8536f7df6 \ - --hash=sha256:6980ceb5c1049d4878632f08ba0bf7234c30e741b0dc9081da0f86eca13189d3 \ - --hash=sha256:69a847a6ffaa86e8af7b9e7037606e05a6f663deec516ad851e8e05d9908d16a \ - --hash=sha256:6ada7bd5bb6511d12177a7b00416ce55caee49fbf8c268f26b909497b534cacb \ - --hash=sha256:70c634e39c5cda0da05c93d6747fdc957599f7743543662b6dbabdd8d3ba8a96 \ - --hash=sha256:7cdd508664430dd51b8888deb6c5b416d8de046b2e11837254378d31febe4a98 \ - --hash=sha256:844d88509c968dd44b30daeefac72b038b1bf31ac372d5106358ab01d393c48b \ - --hash=sha256:847087abe98b3c1ebf1eb49d6ef320dbba75a83ee4f83c94704580f1df007dd4 \ - --hash=sha256:85c9b0c131427470a6423baa0a9330be6fd8c3630cc3ee6fdee03360724cbec5 \ - --hash=sha256:879ae91f2928a13f01a55cfa168acedd2b02b11b4cd8b5bb9223e8cde777ca52 \ - --hash=sha256:887a9fa74418d74d645281ee0edcf60694053bd1bc2ebc49eb5e66bfffc6d107 \ - --hash=sha256:88ebc0783907468f17fca3d7821b30f9c21865a721144eb498cb0ff99a67bcac \ - --hash=sha256:89e50667e7e8c0e7903e4d644a2764fffe9a3a5d6578f72ab7a7b4205bf204b7 \ - --hash=sha256:8a4a3189a99ecdd1c13f42513ab3fc7fa8311b38ba7596dd98537acb8cd9acc3 \ - --hash=sha256:8aaf8ee8f34b677f90742ca089b9c83d64bdc410528767273c816a863ed57327 \ - --hash=sha256:8e4c8fa46aad1a11ae2f8fcd1c90b9d55e18925829ac0d98c5bb107f93351745 \ - --hash=sha256:8fc918cd003ba0d066bf0003deb05a259baaaab4dc9bd4f1207bbbe64224857a \ - --hash=sha256:8fe14e24124ef41220e5992a0f09432f890037df6f93fd3d6b7a0feff2db16b2 \ - --hash=sha256:918db4e34a7ef3d0beee913fa54b34231cc3424676f1c19bdb85f01828d3cd37 \ - --hash=sha256:987cdfcfb97a249abc3601ad53c7de5c370529f1981e4c8c46793e4a1e1bfe8e \ - --hash=sha256:9b9118a78e031a2e4709cd2fcc3028432e89b718db70073a8da574c249b5b249 \ - --hash=sha256:9cf7036dfa2370ccc8651521fcbb40391974841119e9982fa312b552929e6c85 \ - --hash=sha256:a094e9dcafedfb9d333db5cf880304946683f43a6582bb86688f123335122929 \ - --hash=sha256:a416ee898ecbc5d8b283223b4cf4d560f93244f6f7615c1bd67359744b00c166 \ - --hash=sha256:a5d88fa37ba5e8a80ca8d956b9ea03805cfa460223ac94b7d4854ee5e30f3173 \ - --hash=sha256:ace48c5e157c1e58b7de633c5e257285ce85e567ac500c833349c363b3df69d4 \ - --hash=sha256:ad5c53f2e8fcae9144009435ebe3d9832003508cf8935c04542a1b3b8deefa15 \ - --hash=sha256:ad8d372587e659940568afd009afeb72be939c769c552c9b28773d0337251391 \ - --hash=sha256:b193ed199848aa96618cd5959c1582a0bf23cd698b0b900cb0ffe81b02c8659c \ - --hash=sha256:b2e9c2ea2e93223579308263f359eab8837dc340530b860cb59b713651889f14 \ - --hash=sha256:b3aa21bad31db904e0b9055e12c8282df62d43169c4a9d2929407060066ebc74 \ - --hash=sha256:b565f25171e04d4fad950d1fa837133e3af6ea6f509d96166eed745eb0cf63bc \ - --hash=sha256:b56993a7aeb4140c4770f4f7965c9e5af4f024457d06e23c01b0d47501cb18ed \ - --hash=sha256:b6acb765e7c1f2fa08ac9057a33595e26104d7d67046becae184a8f100932dd9 \ - --hash=sha256:b6f366a5ef66a2df4d9e68035cfe9f0eb8473cdfb922c37fac1d169b468607b0 \ - --hash=sha256:b7836aa13721dbdef658aebd11f60d00de633a95726521860fe1f6be75fa225a \ - --hash=sha256:b8fca73e16c49dd972ce3a88278dfa5b93bf91ddef332a46e9443abe21ca2f7c \ - --hash=sha256:b953d9d496d19786f4d46e6ba4b386c6e493e81e40f9c5392332458183b0599d \ - --hash=sha256:bbc458a292aee57d572075f22c035fa32969cdb7987d454e3e34d45a40a0a8b4 \ - --hash=sha256:c1cecea3e477af105f32ef2119b8d895f297492e41d317e60d474bc4bffd62ff \ - --hash=sha256:c1d7fa44aece1fa02b8927441614c96520253a5cad6a96994e3a81e060feed55 \ - --hash=sha256:c1ed17104d1be7f807fdec35ec99777168dd793a09510d753f8710590ba54cdd \ - --hash=sha256:c3c6f6b027d10f84bfe65049028892b5740878edd9eae5fea0d1710b09b1d257 \ - --hash=sha256:c5e0fdb5744caf1036dec5510f543164f2144cb64932251f6dfd42fa872b7f9c \ - --hash=sha256:c60f1de066eb5a0fd8ee5974de4194bb1c2e7692941458807162ffbc39887303 \ - --hash=sha256:c6d9c6e783b348f719b6118bb3f187b2e138e3112576c9679eb458cc8b2e164b \ - --hash=sha256:c940e00e8d3d10932c929d4b8657c2ea47d2560f31874c3e174c0d3488e8b865 \ - --hash=sha256:c9f261ad3cd97257dc1d9355bfbaa7dd703e06574bffa0fa8fe1e31da915ee38 \ - --hash=sha256:d21a07edddb3e0ca12a8b8712abc8452481c3d3db19ae87fc94e9842d005964b \ - --hash=sha256:d363660f9ef8c734495598d2f3e527fb41f745c73159dc0d743402f049fb6836 \ - --hash=sha256:d478a2ca902b6ef28ffc9521e5f0f728d036abe35c0b250ee8ae78cfe7c5e44e \ - --hash=sha256:d571f0b2eec3513734ea31a16ce0f7840c0b85a98e7edfa0e328ed144f9ef78f \ - --hash=sha256:d6b39a2cc5625bbc4fda18919a891eab9aab934eecf83660a90ce20c53621a9a \ - --hash=sha256:d76d62909bfb14521c3f7cfd5b94c0c75ec94b0a11f647d2f604998962ec7b6c \ - --hash=sha256:dab4178a0bc1ef13178832b12db7bc7f562e8f028b2b5be186e370090dc50652 \ - --hash=sha256:db976be51375bca900e008941639448d148c655c9545071965d0571ecc04f5d0 \ - --hash=sha256:ded4fc0edf3de792850cb8b04bbf3c5bd725eeaf9df4c27aad510f6eed9c4e19 \ - --hash=sha256:e006ea703d5c0f3d112b51ba18af73b58209b954acfe3d8da42eacc9a00e4be6 \ - --hash=sha256:e3e5d1802cba785210a4a800e63fcee7a228649a880f3bf7f2aadccb151a834b \ - --hash=sha256:e480d3dac06c89bc2e0fd87524cc38c546ac8b4a38177650745e64acbbcfdeba \ - --hash=sha256:e50af656c15e2723eeb7279c0837e07accc594b95ec18b86821a4d44b51b24bf \ - --hash=sha256:e83ce8008b48762be296f1401f19afd9ea29f3d035d1974e0cecb74e9afbd1df \ - --hash=sha256:ed3b8281c5d0944d939c82db4ec2300409dd69ee087f7a75a94f2e301e855fb4 \ - --hash=sha256:ef250a3f5e93182193f5c927c5e9575b2cb14b80d03e258bc0b89cc5de076b60 \ - --hash=sha256:f1574566457161678297a116fa5d1556c5a4159d64c5ff7c760e7c564bf66f16 \ - --hash=sha256:f26262900edd16272b6360014495e8d68379c6c6e95983f9b7b322dc928a1194 \ - --hash=sha256:f28eac18a8733a124444643a66ac96fef2c0ad65f50034e0a043b90333dc677f \ - --hash=sha256:f54840bea73541652f1170dc63402a5b776fc851ad36a842da9e5163c1f504a0 \ - --hash=sha256:f785f44a44702dea89b28bce5bc82552490694ce4e144e21a4f0545e364d2150 \ - --hash=sha256:f7cc00089b4c21847852c0ad76fb3680f9833b855a0d30bcec94211c435bff6b \ - --hash=sha256:f95bd07f301135771559101c060f558e2cf896c7df00bec050ca7f93bf11585a \ - --hash=sha256:fc8ced733d6cd9af5e412f256a32f7c61cd2d7371280a65c689939ac4572499f \ - --hash=sha256:fd03e38068faeef937cc6761a250a4aaa015564bd0d61481fefcf15586d31825 -requests==2.33.0 \ - --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ - --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b -semver==3.0.4 \ - --hash=sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746 \ - --hash=sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602 -sentence-transformers==5.3.0 \ - --hash=sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2 \ - --hash=sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d -sse-starlette==3.3.4 \ - --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ - --hash=sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1 -starlette==1.0.0 \ - --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \ - --hash=sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b -tenacity==9.1.4 \ - --hash=sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55 \ - --hash=sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a -tornado==6.5.5 \ - --hash=sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9 \ - --hash=sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6 \ - --hash=sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca \ - --hash=sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e \ - --hash=sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07 \ - --hash=sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa \ - --hash=sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b \ - --hash=sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521 \ - --hash=sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7 \ - --hash=sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5 -trl==0.29.1 \ - --hash=sha256:12df5aa22d1deb26942cafdf4cfd75f53d7141942e70c4dc0966687a3617813b \ - --hash=sha256:f9490cd1af93f3dce1cfdfe0fa1de108dbc46a2f6c2f90485cc2b4021c43eef3 -uvicorn==0.42.0 \ - --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 \ - --hash=sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775 -wcwidth==0.6.0 \ - --hash=sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad \ - --hash=sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159 diff --git a/requirements.hashes.wheel.txt b/requirements.hashes.wheel.txt deleted file mode 100644 index b898cae4d..000000000 --- a/requirements.hashes.wheel.txt +++ /dev/null @@ -1,295 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile requirements.wheel.txt --refresh --generate-hashes --index-url https://console.redhat.com/api/pypi/public-rhai/rhoai/3.3/cpu-ubi9/simple/ --python-version 3.12 --emit-index-url --no-deps --no-annotate --universal ---index-url https://console.redhat.com/api/pypi/public-rhai/rhoai/3.3/cpu-ubi9/simple/ - -aiohappyeyeballs==2.6.1 \ - --hash=sha256:eff0885546faac493fa18997b9e5158ea7a56a36ee3222b744bab31808e3f3f8 -aiohttp==3.13.3 \ - --hash=sha256:b170fe2ac5f84453b4b5c18017f6dfb101dc052d7647ed7726cc139b057c12b8 \ - --hash=sha256:baa9ead7e6a15fb47330101ebe4a4fe997b36ede1fa8a33d438e9dfbd8b2035e \ - --hash=sha256:bc56021945f304b3cb066e4e1c1f364747bedaf8fef6e139be63f1b192558330 \ - --hash=sha256:c418ae157c77de1c7314313d767efcdf2243dc0122beddeafd2f3bab73ad0ed8 -aiosignal==1.4.0 \ - --hash=sha256:9d5af4742d4552e8a3f2fbd48237e07cab578e7175af2f952c420d2e98b731f0 -aiosqlite==0.22.1 \ - --hash=sha256:2e85580d41e992ed45e1aafce66ac4c23b7bd7cf842dc5209a39b95c11497115 -annotated-doc==0.0.4 \ - --hash=sha256:cf904ba25da8611ae3274f2af1d6e82275e452113f7774505e723a21957c10d6 -annotated-types==0.7.0 \ - --hash=sha256:fcdc578cef2d4fd8770abc0ceb0241c68c992698aa7a5f945ce67651dd585cfb -anyio==4.12.1 \ - --hash=sha256:e18da418b6421e6735231319ea8ae4262f68b52b8dbcb4699a7a14f89cfb2b15 -asyncpg==0.31.0 \ - --hash=sha256:30b9415c515d7aeeb78a2973398c110b021bbc8c4b664a50309c8a7a59f850e6 \ - --hash=sha256:d5fcd8eb91e0624dbbd391062d529ec24ac41e0665c6aa4df719f6682856b318 \ - --hash=sha256:fb62c7c0a0ecf71ebb9ba83bd67965dd8c8349c1fdb12225b4f76c11f04dcc46 -cffi==2.0.0 \ - --hash=sha256:257e90f733c1a33b9f5ade4a4f47db6a3984de5c2d2654848feca129888ff9d5 \ - --hash=sha256:34cf2187e399eb7baaa20488d0b78ab20be91060ef9ac531685f37478ca1a12a \ - --hash=sha256:e39d7009b1872abbd91bb17057b48fe24057a1026a20ab06b672fbdd4721d789 \ - --hash=sha256:ffe747cf5e265169ad5bd64b4359368f74e445cf8ea3cfe99045450554fb4e13 -chevron==0.14.0 \ - --hash=sha256:215f5e3e7ac75d150eadfc0f8c651b3815dc36813e122484b1ed68e142e5adfb -click==8.3.1 \ - --hash=sha256:a87f0253ce1fb7747cdde1674d73f34241bb4de9fca7a31bc866fb0a8a5f4307 -cryptography==46.0.5 \ - --hash=sha256:2b44c9fd892f763465b2d7782bf310d65c04dab741b1241f5be203ccf022368d \ - --hash=sha256:661cf199efa488e0c5fb4987d36214e11c1fe2dfb842a1a330b1854ff069f8d3 \ - --hash=sha256:88347ad17f60b60e31e5f2b58e37339d88fa90bfa4f0d35528b3bd18d464427b \ - --hash=sha256:bb1b90386c7b5d3d8c9d8d53b207cfbe5c3e639457ff9ebe84f2131918b785a9 -datasets==4.5.0 \ - --hash=sha256:afc7c4ccba966970c71e264f0eb7977bdc7ca8ea9a946e331cf9d3d1d072cb2f -dill==0.4.0 \ - --hash=sha256:e09f4727f6f6c193a79c01826c5221fc26a16022279e4bf38cc3d35ff4197af4 -distro==1.9.0 \ - --hash=sha256:14a63983246dce4a649b0994f909f6e225197c0adb86f68f4be1252309a5c603 -dnspython==2.8.0 \ - --hash=sha256:a7e8d88ef436501e0c667ff4e7251f20297a0c705b83060868bcb259f9f3f41e -docstring-parser==0.17.0 \ - --hash=sha256:21e00984db7761fc91c701857ce566595ca7b65285f469c8da888b509a9ac714 -durationpy==0.10 \ - --hash=sha256:c0a086dd504baa2364a2da2de6d2bd15b2eca7971c66da3c2695006bebfe93dc -einops==0.8.2 \ - --hash=sha256:83fed57f4f0858060494b9e7b32f3d08904225b3f1a1a3bc2a47d43bdf72757e -email-validator==2.3.0 \ - --hash=sha256:3b8ed50b89b305b8e8f5a99e3f7c80e65da28e9c19dff90f4f6d9e5c72f8c081 -faiss-cpu==1.12.0 \ - --hash=sha256:004aa246c589767439e210c275e20f64a5d3e0ee844df7d1ada4612b4c27e348 \ - --hash=sha256:06d06d210105c6769ca1c93c5111ce0a1a7ed2f239b2f666ccb2af7c90f42383 \ - --hash=sha256:4aadb7cfceacda36ff1738fe64af1c16e77f739105bb26246c683572c28db123 \ - --hash=sha256:8a2cc05c9459d0e22d05d1996f2e3a8b25b50cfc0ea9530a5422873048196a83 \ - --hash=sha256:b769fa38427cea3ee56e74af41e04578e5a3bee1389dde6e62116b2a2ac90dda \ - --hash=sha256:d164c65eb35d98f5ea8422b88c9b2645bdc091d3ab9829808699f81fdf7e6c1d -fire==0.7.1 \ - --hash=sha256:833e042e12ce8f590b8a557f90fad4977d355534bc034b30185458ffe6acb638 -frozenlist==1.8.0 \ - --hash=sha256:41a5c624d79ec4157746ada9e19a1da4c3d1b3f5fde9e3f8ac85f7eed9126af9 \ - --hash=sha256:89573b85be77e89a114dc8d1983456091f6c01ff482d31aec2585df0c00de2c5 \ - --hash=sha256:b4fdb636be50043b56a9589b8690a104d7d9ddc72d79058171735fe15eadcc39 \ - --hash=sha256:d7bcd7c1b48b3851acba9e7a11f951c117a654e60004adcbe98425b3f4114057 -fsspec==2025.10.0 \ - --hash=sha256:0b0286f736ac841b0e518cfbd9ec04da01d82726e8ffb5d1a7ae185e041146b2 -google-cloud-core==2.5.0 \ - --hash=sha256:6fbf78b1f08be891d896310309e576f66565a19a79334575db9cececde4fe948 -google-crc32c==1.8.0 \ - --hash=sha256:aa358fd96bbc33eddfccd794e8bf77b7cd1b02b5b857518139dcc0b328392cf6 -google-genai==1.59.0 \ - --hash=sha256:388b25b31c0c00307a947690f21528a7b652e329feb3a14d49cbb16765012313 -google-resumable-media==2.8.0 \ - --hash=sha256:bdf13a0ceec8ba97622165ceeb789e6f0fb7c1614665be246b819811ca661f9c -grpc-google-iam-v1==0.14.3 \ - --hash=sha256:b29d3adb3873130b93f289e44449166586cd684bfdaccc8a4c4d35dc0e3ce9df -grpcio==1.76.0 \ - --hash=sha256:3f935037cced82c09b0c64db9451608646cfb8bb1842dd17551853e94e3f39bc \ - --hash=sha256:47b8a87e383fd6f63f8ba53b2b5c911893f7cfdf5288eff3a41a0bfe4823f5e3 \ - --hash=sha256:70dfe9f5ab1144bca4f53bd7958fdcaf50b220ed21bd378666d9bd1ac97fd371 \ - --hash=sha256:ff138c9e386a0799d88afa389de77df44d9cd7523ad5ac09c0882058e581e1d4 -grpcio-status==1.76.0 \ - --hash=sha256:257105544721149a1fabeb0465dc622add2a7b6f1be07df4ea9ddcb741113cfa -h11==0.16.0 \ - --hash=sha256:a485f5bfa77401603f78cff4db5bb4dbaadb947a3da8eae068cd05b6bb4aab66 -hf-xet==1.2.0 \ - --hash=sha256:a14cc5078bf43efc85ad180ec02372e5a84d134de095c0a9740f7e0407df7866 \ - --hash=sha256:acb8669cac4d9aa8e8ad31a92d99353fe2f0d4463b8aaa54f2b93a33e222149d -httpcore==1.0.9 \ - --hash=sha256:5af3f1b09e795d5548d5030be3d4191e65113cfdac21968c82bf0cddf15ec313 -httpx==0.28.1 \ - --hash=sha256:c75bc8d287ff8e92be7e4359732032ba1b93fa3f920ee1cb179ba09bb613dc7a -httpx-sse==0.4.3 \ - --hash=sha256:74d0e4713b33a61ca0083d00841f00f12d6b3dd311edb62ccc85809b607b9fb5 -idna==3.11 \ - --hash=sha256:e1049ef074501ba8c5d802d712b257889257f6d2f460f959f26c4b2d4375923c -importlib-metadata==8.7.1 \ - --hash=sha256:a3f29411f5f628be21c445082ff4f844a26ff1a0893d6fabaa55509c63ddbfaf -jinja2==3.1.6 \ - --hash=sha256:961c7281585491fb02ca0027b29e9ffc7a1bd7b52a5e03095f3a4e3afc42336e -jiter==0.12.0 \ - --hash=sha256:3ad7e5a7502b62d032b17c4b1bd71ad3049c810358913642abbf5254f3e5c455 \ - --hash=sha256:64f8bd9b9ced14c62d13e64d198e70f55712a775ac7d112849b4b16b3a884489 \ - --hash=sha256:9c42d9b61ab5d2c9203637a243b4187cc28b0101b28461d35006353d621da292 -joblib==1.5.3 \ - --hash=sha256:40d87a5e80b69104a3b8f1d761fdbc077fc7b97a23b08b9fdaddb7a3821b06d1 -jsonschema==4.26.0 \ - --hash=sha256:2601ba467f84ff6ee9c057cbe6a9d7aebefa76cfa747327b37f81d4581f6748f -jsonschema-specifications==2025.9.1 \ - --hash=sha256:065ec57323001f79634013c12250a476952e6ce834a17cfd5227343cec3c4aee -kubernetes==35.0.0 \ - --hash=sha256:2b6649b48b000f7b5b14eb2e6939f0709dddc0a7e4475ed98d4264e191c9f09f -lxml==6.0.2 \ - --hash=sha256:74e99f281150ad91087de3937768f2290a15ae00623d46c16512aa89e826f7ab \ - --hash=sha256:806632cf9f93af4d98229bfd94b4436dba3fba52167cb67b9125d7f7de00fad5 \ - --hash=sha256:90f2c723c7c3c2a2fcc5591ccc6abf3fdf35eda5f3ef84a0ff6f4380dd2e7a9d \ - --hash=sha256:b6836b56be051ebaf5adb68ca280460ecb68cb89ae0bb5d11737ee49b912ac3e -markdown-it-py==4.0.0 \ - --hash=sha256:4ca21586bdc83c96a1318ffc854698e1fdcad60a3e8051d249f10ef210c945c0 -mcp==1.26.0 \ - --hash=sha256:dd60b234158bad67f0743f9928ecbcbc8a67829301a2e793d8a3307ea0d89557 -mdurl==0.1.2 \ - --hash=sha256:fec2de44574adf66cd0d5ec329822fbf05ae011b6ba618c96b178d0eaaf7e249 -mpmath==1.3.0 \ - --hash=sha256:58244de27af87d8fccdb6bc9a17808d4a3f00f5e97152b090272f241f22b1d00 -multidict==6.7.1 \ - --hash=sha256:0f590b4e611242eb68916690dd2d0855b05b788bbaeb6e684c88928f3dfcf9a3 \ - --hash=sha256:4edbf9266edceef23b80ab69bfe575349d6a11a396c36bb47f50f5d75cf4b725 -networkx==3.6.1 \ - --hash=sha256:c37e077bdb955f915337b0cbbe4f70303aca085abe728dbae9f400c5d7b3ae91 -numpy==2.3.5 \ - --hash=sha256:3c09803370223ede9f913428680f123a59bfe95bff6bca3625a42c46983cff1f \ - --hash=sha256:496287a2a8698bfb84129d2da985ae9d801ea986a3ab24ce36261dd9641db032 \ - --hash=sha256:9f44732d61a230ec7f3efb9363e6448325c97c2441ff761e74b7315c72ec3859 \ - --hash=sha256:e3ea2175951df3c16ead190bb3b719af8fcf25c19a5ccb15ac3a0c1a18705bdd -oauthlib==3.3.1 \ - --hash=sha256:df9219023be7c78d77401950592822d1bbc318225d3cd71d02271c549ef1f980 -packaging==26.0 \ - --hash=sha256:76081d1703deb79a44f8c44f23e391b98ab21ea54a2c21b0e8890779e0eaecb7 -pandas==2.3.3 \ - --hash=sha256:308c8ca6262e058136d91a7d4d2bd84dd02e65caf60340fefd20bcf26b97e819 \ - --hash=sha256:6ba245cff03afa84a0bc5e715a11caafab1dc51ca8fe4d827017706c4b62f238 \ - --hash=sha256:cd3d338d360612bed009570e303a59759fb450d163a9d1584c508fa9c22f568a \ - --hash=sha256:e3a18fae723b808514670a4a0172f9939cdbb095abd5eef1f34cf5ae1b99f424 -peft==0.18.1 \ - --hash=sha256:026817e68c93fcc0569360afa0ee4fb74b06b0a4268240f922bc2bc0a691bcc1 -pillow==12.1.1 \ - --hash=sha256:58c0a6787ac12753fba61035713f939f33946c970fe48a5756ed1a36c22d2e79 \ - --hash=sha256:98ab177b9de8751ec5b1dbb7597b45c7edc358f7e16e5764ae93c976c6433f9f \ - --hash=sha256:dd45829dc58e931ebef6a4b7eb122efef838a8d37848d5ec857a79d4c7e8f543 \ - --hash=sha256:fcf5fa3497ec5c32843367d1133bc6b6d273e82d2fc86cd6d309cc09e7e457cf -prometheus-client==0.24.1 \ - --hash=sha256:fe601e041eac55bad8f46da2f3c54f2ab6cd8a8272d9595742c83980e95ed5e4 -prompt-toolkit==3.0.52 \ - --hash=sha256:c329e921742d68b12ff8543ca7004be7b4ccd8a69317bea8d9a6e524dd48cdbe -propcache==0.4.1 \ - --hash=sha256:10f7bf66817595de6682fd428cf2aa7892c0b0b45475585e8029655ab50c0510 \ - --hash=sha256:3ccf1a18d315e7e234aee592a5dddd10951fa41f8a8658021dbe7ac413cca797 \ - --hash=sha256:3f5189a8cf5fd36f414b4d9bf244e61a1eca49ac57942c7e930250df94b2340a \ - --hash=sha256:42ecf4d140bbc503d90097af47bfc9c7e2a88faaad70694ea2bf50ad3da35c41 -psycopg2-binary==2.9.11 \ - --hash=sha256:0badf162b98436fdb9aa273e2901db462a6ffa081904e9d722429b75e01532d9 \ - --hash=sha256:623cff634940e5d248125a1b8d74a19625db0c11c6aecd8db007940ccb409436 \ - --hash=sha256:bf7588449bc9604e13c7fc2a374c359a82784a265776b2975fd013588eba032e -pyarrow==23.0.0 \ - --hash=sha256:5c8077c77ed2d0ef9ec150cc2e47dd98e3e49e6f7d5e3ee84e718ee31b45cf85 \ - --hash=sha256:f5af9838bbfefa2535c3ae9bf4fbfaab63367994c1e65243b1830e41b943b366 \ - --hash=sha256:f8eea464a5138cfe7b32b5422caedaca6a9fe4b20af1ed917f2a6cf9ec802d94 -pyasn1-modules==0.4.2 \ - --hash=sha256:cda22a851735d664e92c8354a30405d88c5a1aa7d6313ba6c583bc3034654937 -pycparser==3.0 \ - --hash=sha256:86072d0cf4bf7e125171d79036c2126a2fa377687520e834987c2de4b4e5a9e9 -pydantic==2.12.5 \ - --hash=sha256:ba78cfc9d85e40047d67d32828da04ddbe9a27bd0718983938629bfca0d3cc6c -pydantic-core==2.41.5 \ - --hash=sha256:280238d34598c8e0eac77c2d96456cb5657f4afc3235d6afa15859a143cc3885 \ - --hash=sha256:2c1462d1bdf9b8b13c8afa43856d7163c5a2567e9f05fcd5d26f54f23f35edf0 \ - --hash=sha256:3713d923ca3dd74e51ad6ce5be8e2bb9b8398cbc3cdbe7bd959282e9e9694fff \ - --hash=sha256:41f74ba1ea18462d7cc27155581a9b23165adc7b009c6d467542274d63db163c -pygments==2.19.2 \ - --hash=sha256:f928b5b8b19c65a1b6ed1f285d1db53fa203d6ba34a3783a92c41e41e2f33135 -python-dateutil==2.9.0.post0 \ - --hash=sha256:c112f3de33b4ef01360132ef116d72df91cdf628f136b70f1fc9a9eb19137cb9 -python-multipart==0.0.22 \ - --hash=sha256:b5fc2a9738209bd168ef0c746ca0ee5eb66f2c69aeeb689e0dbc15b36c779aac -pyyaml==6.0.3 \ - --hash=sha256:84a2acfe1e8dfcf8be95fa61611ea7637f8bfcff49761a6a8c396aaa0b6d94a9 \ - --hash=sha256:bcaf1b152ce8cd6ec4ff56f8ca929f176eb01e6aa472fe10b3ea31f19e15fb39 \ - --hash=sha256:c22e4a0d2bb69fddc73b3b52f1540533df1f206a08f52ea8cae49337615b55d2 \ - --hash=sha256:c99b924c5311933fa398cc53c617f99705cf2d96226d023f75e95e15e753f81f -referencing==0.37.0 \ - --hash=sha256:bd019ff74869f84a893b7e50b84ce3d0db1e465ceb3a336403011c6467ffacb0 -requests-oauthlib==2.0.0 \ - --hash=sha256:4fd6526c6de7a0bd036e84563b7a1523d92ba1f62e32cad65ccef9b2523bbc33 -rpds-py==0.30.0 \ - --hash=sha256:16a18b2b2566d88ccdabe156c2fd0b6bcc4fb172d084ec7e4c1ab502cb8a5cfd \ - --hash=sha256:613bc173bc12f35e40c3d1c1e6252555eaa48ea9425ee0b01fb151f3ca8a2a05 \ - --hash=sha256:6c94c5f1c2501e50c22b7c993b083aeef4d342974d3058763296fb4646d8a059 \ - --hash=sha256:75a565fc839958562ab5ad648c3a4bd0c46874a5556acb48da423f91c47c355b -safetensors==0.7.0 \ - --hash=sha256:18abcf37ffae5f85a81ca46c440fdc5f38ae0938ff0f4a4de14e37386ed248e7 \ - --hash=sha256:6833f019f20c8f0bb790ae3f0fd088f50c9fe4e051106dc3e59df5a953f68532 \ - --hash=sha256:6aea22e3ce1ed41d56c0e50f1cb504fe47a50ec7a1d50135d76fd233bbe4b1e3 \ - --hash=sha256:bc9d119ad82379f387c1264de35e6d489b7196c431b23e92b2b0959eae8494c7 -scikit-learn==1.8.0 \ - --hash=sha256:4c73965fdde273763f87334dfb0d773cb9196e2eb9dd21a0aabd86cf4da2adb3 \ - --hash=sha256:5641af1f1bcede6f6d894f50418b2d820f3a3b82b3a5cb702621e6a386eef935 \ - --hash=sha256:5d27f41e0a981a58fcc00f09ef9c00fd68a2e13c218768d0ed69c1a247c8855f \ - --hash=sha256:7147bcc3428006a9b5fc0f0934b70c555ddd9f8ebc4b3ee4aaebb44c5e87a3dd -scipy==1.17.0 \ - --hash=sha256:0e35bf4bcd96fb22c9007cb651e3fa7a696bf90986fd220607505f5c2b7f17e7 \ - --hash=sha256:92b4828228173816adc054c97a539c41f396558fbab67dcaae1e4195c6d6253f \ - --hash=sha256:a1b3d25c892207a7626f0634fe768bd71b5f09cbb393be07d82fc44cced109ac \ - --hash=sha256:f1c7b6ff095ed94c422bce52aa0ca8c8aea18922877853bbacaf8b7947e02510 -setuptools==80.9.0 \ - --hash=sha256:3a3c26f9000ab213d87be4efa0f1926fb0975237ae9e8e7cc68c9d7fcba6c294 -six==1.17.0 \ - --hash=sha256:33f558442b372864d53b1813933f5d624876f418521b1b89624ea6e3d79f0e97 -sniffio==1.3.1 \ - --hash=sha256:79fc97358f9f993658f5285996c92b070d9b2b9cdd789daefd3e0607d518097b -sqlalchemy==2.0.45 \ - --hash=sha256:52a0fb297270ecc4066303762220717240f5e427298345e19835d21319fc9522 \ - --hash=sha256:69ada3936fac5c0c15e765e71fae9b1177c9c957eb7fbd5210f63d7c05aa26c3 \ - --hash=sha256:6ea7264891e42cfdb069a27eefa2438214a54051d8b5b1b00209af9f944c3480 \ - --hash=sha256:7d691e498146a57a54f347385729b57e98b2e548d6fd6e25fdcbdf6e48a08ae4 -sympy==1.14.0 \ - --hash=sha256:92de12bc45a8930ecab22227dea3f4192e15d9f4555c9a9d52d312865748afbd -termcolor==3.3.0 \ - --hash=sha256:2ed14ed07b1ab932881a26035e8f6a9e6251052289ecf8325e535bc17a03240d -threadpoolctl==3.6.0 \ - --hash=sha256:fc79267be059d5a0aaacf924656a1523599e7fc844c44608db5fe1cb2f152c4f -tiktoken==0.12.0 \ - --hash=sha256:019dfefcb4f2899eb8c7fd4d170c3ed335113579de129f55526fcdc3b43ef5ba \ - --hash=sha256:9d341a1e06d325151f05991a0a1616c12918132fda42b7601d25c9d02726e5b3 \ - --hash=sha256:aee67dcabd42538b82abaf27a1d5b74da0eb2da37fa9c9c117ca99d9146e2937 -tokenizers==0.22.2 \ - --hash=sha256:109c12a85bc794db2d335e9845198fd4dfbdd25a5e47d944ada5ee28d720a01c \ - --hash=sha256:914385483cfc1f8a38e8141e9370ca315484464afda41eb3959cd4d36ad2217b \ - --hash=sha256:c6123f7e43990992323d4cd8ee0d463383a7dffe3c03e1f4483b446034910faf \ - --hash=sha256:c883273341b96903ddeda79cb247a6f3ea316bd668ee2dd93d86e6fb57fc35b3 -torch==2.9.1 \ - --hash=sha256:28171ed367139717fe7b8d6918c86d0fe328a7faa5704f5951283ae3b01796a1 \ - --hash=sha256:3ebd43c74b5c0833e2d6c842cb5416bacb928184e362c319eb79223d06bb2fa1 \ - --hash=sha256:9920139df4000ea99e1d71742d4bce1d7b807a5f45cf1a7cbff02c8dd8295bdb \ - --hash=sha256:9bd4844a0cf3f199351830697973a168ad2fd3a99e77b150ca4a1582067dc633 \ - --hash=sha256:ccb4a5b3c15819df80d96d2474b053306a2a4eba0301337c4aa56a58cbe45e10 \ - --hash=sha256:e42ab849b64444059f5eda352d61c9c3a078f30797e48e2972857182c9a00cf8 -tqdm==4.67.3 \ - --hash=sha256:d798b33fcc041b9a42c57f462b9c068a5a15c2dbcef1c87695d80c7074770a4d -transformers==4.57.6 \ - --hash=sha256:7c35d073118fad72b1745bd3dad3149d9bf2cacca3b16a7ed8f47f1a0bec119d -tree-sitter==0.25.2 \ - --hash=sha256:05bc490c99a846e0e91d9dd0c5067de48b35b46d4c3f18fc2cc9ac08a4ec71fe \ - --hash=sha256:3aa0cf6b0964495ada302c3eb966bf6f41a01279d8e31c019e4b0129a93d5a55 \ - --hash=sha256:aadac5c0356e824916ca95f8f794a6ef88105748eed6e8fdb7fb4690024d4336 -triton==3.5.1 \ - --hash=sha256:7b1a1b9ab53eafe7a65ae25861ac7df5541ea706b3baebe2306580127825e222 \ - --hash=sha256:f45cd8a990cde562e64b6ef00969dc3751a49e90651216e7f7b44ba997c9e19d \ - --hash=sha256:fce4c06d8e47ccd706790271cc3f2cea42c7e709cfb68e6abe760d2a56baeeb4 -typing-extensions==4.15.0 \ - --hash=sha256:00877ead43795d4b767026270fa129816363c9a95b6febe68f5ac41d5096114b -typing-inspection==0.4.2 \ - --hash=sha256:4baec0c9a4ad5cc8a85c71c387e98ff9ecc7b85b6e538e204343940090be3b63 -tzdata==2025.3 \ - --hash=sha256:896a9fad76b5de068b461ef456f09931bb94c8455835767d100298c75dedd18b -urllib3==2.6.3 \ - --hash=sha256:a5bd1dd5b4c7b1da77f93abcd09f8619209b2f11fe699382f6cb79b34dc89437 -websocket-client==1.9.0 \ - --hash=sha256:0cb04d0516ad9d4a5941144e68344813f1da89a2270b0634b4b71f1e892f4caf -websockets==15.0.1 \ - --hash=sha256:1ad83cda272b5a5d77faa3c4b43c6cbe6cfa3ec7a386899fefd14df455d8a51c \ - --hash=sha256:734b81ff7d1e22130a20f58af82a69e413025de9804e967cab503fe17332b14c \ - --hash=sha256:933ac176329fedf18d4207defff775829b9de301075bc297c169081ece1e1d9d \ - --hash=sha256:b022c4ed3a5a96300b56e9f0b18a302d266fac0a7819d906cca9e702f93976bf -wrapt==1.17.3 \ - --hash=sha256:0953273f64ed6dc39821a1d06409958d5d5bf53a699f3931cb1a81c656fdd720 \ - --hash=sha256:15c6bffa4386f4927f1e1c0b4e2e4604c10d709199ecbd3f69ae1e659f2d96d8 \ - --hash=sha256:1649eb282f55f5d90154086a34fef4922aa31b28233e03ce396b7d8a778a5904 -xxhash==3.6.0 \ - --hash=sha256:122ea61c85a8725231030b26650ae35b1ae709f154e1e8f5274b2cd469638a14 \ - --hash=sha256:a426db4666ab784b369beab268f41bd7b2505390b1a6f7ae0bbc295a103c4c83 \ - --hash=sha256:bb363fc4152e194b2f68829da7d783d9b5eee9577c33f6007c78d25517a229ca \ - --hash=sha256:edac8303f5c2d0bec5632a6e6cb9c56f3cd548e94960c5b9468c654d5a73c653 -yarl==1.22.0 \ - --hash=sha256:8e78442a1a40257173cbd3b9ec78a1ded7ab02a1fcb23f3d42686bd5afb184cc \ - --hash=sha256:b59c74a17b24908333ce8e0a348fafe85a1bdb1d524cc74d9c25161674dd358a \ - --hash=sha256:cee8af2825e29b4b31cce0c7272c29b66717ab164e3e2a5dc8dbe173c2901db2 \ - --hash=sha256:eb5d90c48762dd433d86eb6e3c6b4965fc3dff440c472e89047a628dd29271ac -zipp==3.23.0 \ - --hash=sha256:2f6742e089020240e014478d428fd79059270c56a2f7947d37c6f1d2ae639174 diff --git a/requirements.hermetic.txt b/requirements.hermetic.txt deleted file mode 100644 index 5854ded06..000000000 --- a/requirements.hermetic.txt +++ /dev/null @@ -1,2 +0,0 @@ -uv==0.9.16 -pip==25.3 \ No newline at end of file diff --git a/requirements.overrides.txt b/requirements.overrides.txt deleted file mode 100644 index a403794aa..000000000 --- a/requirements.overrides.txt +++ /dev/null @@ -1,23 +0,0 @@ -# override these package to the version available on RHOAI wheels index: -# https://console.redhat.com/api/pypi/public-rhai/rhoai/3.3/cpu-ubi9/simple -transformers==4.57.6 -tokenizers==0.22.2 -scipy==1.17.0 -aiohttp==3.13.3 -aiosqlite==0.22.1 -cryptography==46.0.5 -anyio==4.12.1 -datasets==4.5.0 -pandas==2.3.3 -pyarrow==23.0.0 -pillow==12.1.1 -faiss-cpu==1.12.0 -sqlalchemy==2.0.45 -setuptools==80.9 -jiter==0.12 -google-genai==1.59.0 -torch==2.9.1 -hf-xet==1.2.0 -yarl==1.22.0 -grpcio==1.76.0 -grpcio-status==1.76.0 diff --git a/scripts/generate-rpm-lock.sh b/scripts/generate-rpm-lock.sh new file mode 100755 index 000000000..f4ffa436c --- /dev/null +++ b/scripts/generate-rpm-lock.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +set -e + +DEFAULT_BASE_IMAGE="registry.redhat.io/rhai/base-image-cpu-rhel9:3.4" +BUILD_ARGS_FILE=".konflux/build-args-konflux.conf" +INPUT_FILE=".konflux/rpms.in.yaml" +OUTPUT_FILE=".konflux/rpms.lock.yaml" +CONTAINER_IMAGE="registry.access.redhat.com/ubi9/ubi" +REDHAT_REPO_FILE=".konflux/redhat.repo" + +if command -v podman &>/dev/null; then + CONTAINER_RUNTIME="podman" +elif command -v docker &>/dev/null; then + CONTAINER_RUNTIME="docker" +else + echo "Error: Neither podman nor docker found. Please install one of them." + exit 1 +fi + +if [[ -f "$BUILD_ARGS_FILE" ]]; then + EXTRACTED_BASE_IMAGE=$(grep "^BUILDER_BASE_IMAGE=" "$BUILD_ARGS_FILE" | cut -d'=' -f2) + if [[ -n "$EXTRACTED_BASE_IMAGE" ]]; then + BASE_IMAGE="$EXTRACTED_BASE_IMAGE" + echo "Using base image from $BUILD_ARGS_FILE: $BASE_IMAGE" + else + BASE_IMAGE="$DEFAULT_BASE_IMAGE" + echo "BUILDER_BASE_IMAGE not found in $BUILD_ARGS_FILE, using default: $BASE_IMAGE" + fi +else + BASE_IMAGE="$DEFAULT_BASE_IMAGE" + echo "$BUILD_ARGS_FILE not found, using default base image: $BASE_IMAGE" +fi + +usage() { + echo "Usage: $0 -a ACTIVATION_KEY -g ORG_ID [-i BASE_IMAGE] [-f INPUT_FILE] [-O OUTPUT_FILE]" + echo "" + echo "Required:" + echo " -a ACTIVATION_KEY Red Hat activation key for subscription-manager" + echo " -g ORG_ID Red Hat organization ID for subscription-manager" + echo "" + echo "Options:" + echo " -i BASE_IMAGE Base container image for rpm-lockfile-prototype (default: $BASE_IMAGE)" + echo " -f INPUT_FILE Input RPM specification file (default: $INPUT_FILE)" + echo " -O OUTPUT_FILE Output lock file (default: $OUTPUT_FILE)" + echo " -h Show this help message" + echo "" + echo "Environment variables:" + echo " REGISTRY_USERNAME Username for registry authentication (skopeo login)" + echo " REGISTRY_PASSWORD Password for registry authentication (skopeo login)" + exit 1 +} + +ACTIVATION_KEY="" +ORG_ID="" + +while getopts "a:g:i:f:O:h" opt; do + case $opt in + a) ACTIVATION_KEY="$OPTARG" ;; + g) ORG_ID="$OPTARG" ;; + i) BASE_IMAGE="$OPTARG" ;; + f) INPUT_FILE="$OPTARG" ;; + O) OUTPUT_FILE="$OPTARG" ;; + h) usage ;; + *) usage ;; + esac +done + +if [[ -z "$ACTIVATION_KEY" || -z "$ORG_ID" ]]; then + echo "Error: Both activation key (-a) and organization ID (-g) are required." + usage +fi + +if [[ ! -f "$INPUT_FILE" ]]; then + echo "Error: Input file '$INPUT_FILE' not found." + exit 1 +fi + +if [[ ! -f "$REDHAT_REPO_FILE" ]]; then + echo "Error: Red Hat repo file '$REDHAT_REPO_FILE' not found." + exit 1 +fi + +echo "Using BASE_IMAGE: $BASE_IMAGE" +echo "Using INPUT_FILE: $INPUT_FILE" +echo "Using OUTPUT_FILE: $OUTPUT_FILE" +echo "Using CONTAINER_IMAGE: $CONTAINER_IMAGE" +echo "Using CONTAINER_RUNTIME: $CONTAINER_RUNTIME" + +CONTAINER_NAME="rpm-lockfile-generator-$$" +WORKDIR="/workdir" + +cleanup() { + echo "Cleaning up container..." + $CONTAINER_RUNTIME exec "$CONTAINER_NAME" subscription-manager unregister 2>/dev/null || true + $CONTAINER_RUNTIME rm -f "$CONTAINER_NAME" 2>/dev/null || true +} +trap cleanup EXIT + +echo "Starting container..." +$CONTAINER_RUNTIME run -d --name "$CONTAINER_NAME" "$CONTAINER_IMAGE" sleep infinity + +echo "Registering system with subscription-manager..." +$CONTAINER_RUNTIME exec "$CONTAINER_NAME" subscription-manager register \ + --activationkey="$ACTIVATION_KEY" \ + --org="$ORG_ID" + +echo "Disabling rhel-9-for-x86_64-appstream-eus-rpms repo" +$CONTAINER_RUNTIME exec "$CONTAINER_NAME" subscription-manager repos --disable=rhel-9-for-x86_64-appstream-eus-rpms --disable=codeready-builder-for-rhel-9-x86_64-eus-rpms + +echo "Installing packages..." +$CONTAINER_RUNTIME exec "$CONTAINER_NAME" dnf install -y skopeo git make python3-pip + +if [[ -n "$REGISTRY_USERNAME" && -n "$REGISTRY_PASSWORD" ]]; then + echo "Logging into registry with skopeo..." + REGISTRY_HOST=$(echo "$BASE_IMAGE" | cut -d'/' -f1) + printf '%s\n' "$REGISTRY_PASSWORD" | $CONTAINER_RUNTIME exec -i "$CONTAINER_NAME" skopeo login "$REGISTRY_HOST" \ + --username "$REGISTRY_USERNAME" \ + --password-stdin +fi + +echo "Installing rpm-lockfile-prototype..." +$CONTAINER_RUNTIME exec "$CONTAINER_NAME" python3 -m pip install --user \ + https://github.com/konflux-ci/rpm-lockfile-prototype/archive/refs/tags/v0.21.0.tar.gz + +CONTAINER_INPUT="$(basename "$INPUT_FILE")" +CONTAINER_OUTPUT="$(basename "$OUTPUT_FILE")" + +echo "Creating workdir and copying files..." +$CONTAINER_RUNTIME exec "$CONTAINER_NAME" mkdir -p "$WORKDIR" +$CONTAINER_RUNTIME cp "$INPUT_FILE" "$CONTAINER_NAME:$WORKDIR/$CONTAINER_INPUT" +$CONTAINER_RUNTIME cp "$REDHAT_REPO_FILE" "$CONTAINER_NAME:$WORKDIR/$(basename "$REDHAT_REPO_FILE")" + +echo "Running rpm-lockfile-prototype..." +# shellcheck disable=SC2016 +$CONTAINER_RUNTIME exec -w "$WORKDIR" "$CONTAINER_NAME" bash -c ' + DNF_VAR_SSL_CLIENT_KEY=$(find /etc/pki/entitlement -type f -name "*key.pem" | head -1) + export DNF_VAR_SSL_CLIENT_KEY + DNF_VAR_SSL_CLIENT_CERT="${DNF_VAR_SSL_CLIENT_KEY//-key/}" + export DNF_VAR_SSL_CLIENT_CERT + /root/.local/bin/rpm-lockfile-prototype \ + --image "'"$BASE_IMAGE"'" \ + --outfile "'"$CONTAINER_OUTPUT"'" \ + "'"$CONTAINER_INPUT"'" +' + +echo "Copying output file from container..." +$CONTAINER_RUNTIME cp "$CONTAINER_NAME:$WORKDIR/$CONTAINER_OUTPUT" "$OUTPUT_FILE" + +echo "Successfully generated $OUTPUT_FILE" diff --git a/scripts/konflux_requirements.sh b/scripts/konflux_requirements.sh index 67cff3e7e..210de33cc 100755 --- a/scripts/konflux_requirements.sh +++ b/scripts/konflux_requirements.sh @@ -12,7 +12,7 @@ WHEEL_FILE="requirements.wheel.txt" SOURCE_HASH_FILE="requirements.hashes.source.txt" WHEEL_HASH_FILE="requirements.hashes.wheel.txt" BUILD_FILE="requirements-build.txt" -RHOAI_INDEX_URL="https://console.redhat.com/api/pypi/public-rhai/rhoai/3.3/cpu-ubi9/simple/" +RHOAI_INDEX_URL="https://packages.redhat.com/api/pypi/public-rhai/rhoai/3.4/cpu-ubi9/simple/" # extra wheels to be included in the wheel list, often come from build-time dependencies EXTRA_WHEELS="uv,pip,maturin" diff --git a/scripts/konflux_resolve.py b/scripts/konflux_resolve.py new file mode 100644 index 000000000..94173ba43 --- /dev/null +++ b/scripts/konflux_resolve.py @@ -0,0 +1,1331 @@ +#!/usr/bin/env python3 +"""Policy-driven dependency resolver for Hermeto/Cachi2 hermetic builds. + +Enforces: RHOAI wheel > PyPI sdist > PyPI wheel (last resort). +Usage: python3 scripts/konflux_resolve.py --profile cpu|cuda [--verbose | --quiet] +""" + +from __future__ import annotations + +import argparse +import json +import logging +import os +import re +import subprocess +import time +import tomllib +import urllib.request +from collections import deque +from collections.abc import Sequence +from html.parser import HTMLParser +from typing import Any + +logger = logging.getLogger("konflux_resolve") + +# --------------------------------------------------------------------------- +# Task 2 — Version parsing and constraint matching (PEP 440) +# --------------------------------------------------------------------------- + +_PRE_RELEASE_MAP = { + "a": "a", + "alpha": "a", + "b": "b", + "beta": "b", + "c": "rc", + "rc": "rc", + "dev": "dev", +} + +_PRE_ORDER = {"dev": 0, "a": 1, "b": 2, "rc": 3, "final": 4} + +_VERSION_RE = re.compile( + r"^(\d+)" + r"(?:\.(\d+))?" + r"(?:\.(\d+))?" + r"(?:\.\d+)*" # extra numeric segments (ignored after third) + r"(?:[-.]?" + r"(?P
a|alpha|b|beta|c|rc|dev)"
+    r"(?P\d+)?)?"
+    r"(?:\.post(?P\d+))?"
+    r"$",
+    re.IGNORECASE,
+)
+
+
+def parse_version(
+    version_str: str,
+) -> tuple[int, int, int, tuple[int, int], tuple[int, int]]:
+    """Parse a PEP 440 version string into a comparable tuple.
+
+    Returns ``(major, minor, micro, (pre_label, pre_num), (post_label, post_num))``.
+    *pre_label* is one of ``"dev"``, ``"a"``, ``"b"``, ``"rc"``, ``"final"``
+    (where ``"final"`` means no pre-release suffix); *post_label* is ``"post"``
+    or ``"final"``.  The labels are ordered so that tuple comparison gives the
+    correct PEP 440 ordering.
+    """
+    version_str = version_str.strip()
+    if "+" in version_str:
+        version_str = version_str.split("+", 1)[0]
+    if version_str.endswith(".*"):
+        version_str = version_str[:-2]
+    m = _VERSION_RE.match(version_str)
+    if m is None:
+        raise ValueError(f"Cannot parse version: {version_str!r}")
+
+    major = int(m.group(1))
+    minor = int(m.group(2) or 0)
+    micro = int(m.group(3) or 0)
+
+    pre_raw = m.group("pre")
+    if pre_raw is not None:
+        pre_label = _PRE_RELEASE_MAP[pre_raw.lower()]
+        pre_num = int(m.group("pre_num") or 0)
+    else:
+        pre_label = "final"
+        pre_num = 0
+
+    post_raw = m.group("post")
+    pre_tag: tuple[int, int] = (_PRE_ORDER[pre_label], pre_num)
+    post_tag: tuple[int, int] = (1, int(post_raw)) if post_raw is not None else (0, 0)
+
+    return (major, minor, micro, pre_tag, post_tag)
+
+
+def _parse_specifier(spec: str) -> tuple[str, str]:
+    """Split ``>=1.2.3`` into ``(">=", "1.2.3")``."""
+    spec = spec.strip().strip("()")
+    for op in ("~=", "==", "!=", ">=", "<=", ">", "<"):
+        if spec.startswith(op):
+            return op, spec[len(op) :].strip()
+    if spec and spec[0].isdigit():
+        return "==", spec
+    raise ValueError(f"Unknown version operator in {spec!r}")
+
+
+_CMP_OPS: dict[str, Any] = {
+    "==": lambda v, c: v == c,
+    "!=": lambda v, c: v != c,
+    ">=": lambda v, c: v >= c,
+    "<=": lambda v, c: v <= c,
+    ">": lambda v, c: v > c,
+    "<": lambda v, c: v < c,
+}
+
+
+def _check_single_specifier(
+    version: str,
+    v: tuple[int, int, int, tuple[int, int], tuple[int, int]],
+    op: str,
+    ver_str: str,
+) -> bool:
+    """Return False if *v* violates the single specifier ``op ver_str``."""
+    if op == "==" and ver_str.endswith(".*"):
+        prefix = ver_str[:-2]
+        parts = prefix.split(".")
+        return version.split(".")[: len(parts)] == parts
+
+    if op == "~=":
+        parts = ver_str.split(".")
+        c = parse_version(ver_str)
+        upper_parts = parts[:-1]
+        upper_parts[-1] = str(int(upper_parts[-1]) + 1)
+        upper = parse_version(".".join(upper_parts))
+        return v >= c and v < upper
+
+    c = parse_version(ver_str)
+    check = _CMP_OPS.get(op)
+    if check is not None:
+        return bool(check(v, c))
+    return True
+
+
+def version_satisfies(version: str, constraint: str) -> bool:
+    """Check whether *version* satisfies a comma-separated PEP 440 constraint."""
+    constraint = constraint.strip()
+    if not constraint:
+        return True
+
+    v = parse_version(version)
+    for spec in constraint.split(","):
+        spec = spec.strip()
+        if not spec:
+            continue
+        op, ver_str = _parse_specifier(spec)
+        if not _check_single_specifier(version, v, op, ver_str):
+            return False
+    return True
+
+
+def merge_constraints(existing: str | None, new: str) -> str:
+    """Merge two constraint strings by comma-joining."""
+    if not existing:
+        return new
+    return f"{existing},{new}"
+
+
+# ---------------------------------------------------------------------------
+# Task 3 — Package name normalization and pyproject.toml parsing
+# ---------------------------------------------------------------------------
+
+_NORMALIZE_RE = re.compile(r"[-_.]+")
+
+
+def normalize_name(name: str) -> str:
+    """PEP 503 normalization: lowercase, replace runs of ``-``, ``.``, ``_`` with ``-``."""
+    return _NORMALIZE_RE.sub("-", name).lower()
+
+
+def _parse_dep_string(dep: str) -> tuple[str, str, str]:
+    """Parse ``name[extras]>=1.0; marker`` into ``(normalized_name, version_spec, marker)``."""
+    marker = ""
+    if ";" in dep:
+        dep, marker = dep.split(";", 1)
+        marker = marker.strip()
+
+    dep = dep.strip()
+    # Strip extras: name[extras]>=... → name>=...
+    dep = re.sub(r"\[.*?\]", "", dep)
+
+    match = re.match(r"^([A-Za-z0-9][-A-Za-z0-9_.]*)", dep)
+    if match is None:
+        raise ValueError(f"Cannot parse dependency: {dep!r}")
+
+    name = normalize_name(match.group(1))
+    version_spec = dep[match.end() :].strip()
+
+    return name, version_spec, marker
+
+
+def parse_direct_deps(pyproject_path: str) -> list[tuple[str, str]]:
+    """Parse ``[project].dependencies`` from a TOML file.
+
+    Returns ``[(normalized_name, version_spec), ...]``.
+    """
+    with open(pyproject_path, "rb") as f:
+        data = tomllib.load(f)
+
+    raw_deps: list[str] = data.get("project", {}).get("dependencies", [])
+    result: list[tuple[str, str]] = []
+    for dep_str in raw_deps:
+        name, spec, _marker = _parse_dep_string(dep_str)
+        result.append((name, spec))
+    return result
+
+
+# ---------------------------------------------------------------------------
+# Task 4 — PEP 503 simple index parser
+# ---------------------------------------------------------------------------
+
+
+class _LinkCollector(HTMLParser):
+    """Collect ``href`` attributes from ```` tags."""
+
+    def __init__(self) -> None:
+        super().__init__()
+        self.hrefs: list[str] = []
+        self._texts: list[str] = []
+        self._in_a = False
+        self.link_texts: list[str] = []
+
+    def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
+        if tag == "a":
+            for attr_name, attr_val in attrs:
+                if attr_name == "href" and attr_val is not None:
+                    self.hrefs.append(attr_val)
+            self._in_a = True
+            self._texts = []
+
+    def handle_data(self, data: str) -> None:
+        if self._in_a:
+            self._texts.append(data)
+
+    def handle_endtag(self, tag: str) -> None:
+        if tag == "a" and self._in_a:
+            self.link_texts.append("".join(self._texts).strip())
+            self._in_a = False
+
+
+_WHEEL_RE = re.compile(
+    r"^(?P[A-Za-z0-9][-A-Za-z0-9_.]*?)"
+    r"-(?P\d[A-Za-z0-9_.+]*?)"
+    r"(?:-(?P\d[A-Za-z0-9_.]*)?)?"
+    r"-(?P[A-Za-z0-9_.]+)"
+    r"-(?P[A-Za-z0-9_.]+)"
+    r"-(?P[A-Za-z0-9_.]+)"
+    r"\.whl$"
+)
+
+_SDIST_RE = re.compile(
+    r"^(?P[A-Za-z0-9][-A-Za-z0-9_.]*?)"
+    r"-(?P\d[A-Za-z0-9_.]*)"
+    r"(?:\.tar\.gz|\.zip)$"
+)
+
+
+class SimpleIndexParser:
+    """Parse PEP 503 Simple Repository API HTML pages."""
+
+    @staticmethod
+    def parse_root(html: str) -> list[str]:
+        """Return list of package names from root index page."""
+        collector = _LinkCollector()
+        collector.feed(html)
+        return collector.link_texts
+
+    @staticmethod
+    def parse_package_page(html: str) -> list[dict[str, Any]]:
+        """Return list of entry dicts from a per-package page.
+
+        Each dict has keys: ``filename``, ``sha256``, ``version``, ``is_wheel``,
+        and for wheels: ``python_tag``, ``abi_tag``, ``platform_tag``.
+        """
+        collector = _LinkCollector()
+        collector.feed(html)
+        entries: list[dict[str, Any]] = []
+
+        for href, link_text in zip(collector.hrefs, collector.link_texts):
+            filename = link_text.strip()
+            if not filename:
+                filename = href.rsplit("/", 1)[-1].split("#")[0]
+
+            sha256 = ""
+            if "#sha256=" in href:
+                sha256 = href.split("#sha256=", 1)[1]
+
+            whl_m = _WHEEL_RE.match(filename)
+            if whl_m:
+                entries.append(
+                    {
+                        "filename": filename,
+                        "sha256": sha256,
+                        "version": whl_m.group("version"),
+                        "is_wheel": True,
+                        "python_tag": whl_m.group("python"),
+                        "abi_tag": whl_m.group("abi"),
+                        "platform_tag": whl_m.group("platform"),
+                    }
+                )
+                continue
+
+            sdist_m = _SDIST_RE.match(filename)
+            if sdist_m:
+                entries.append(
+                    {
+                        "filename": filename,
+                        "sha256": sha256,
+                        "version": sdist_m.group("version"),
+                        "is_wheel": False,
+                    }
+                )
+
+        return entries
+
+
+# ---------------------------------------------------------------------------
+# Task 5 — Wheel compatibility checker
+# ---------------------------------------------------------------------------
+
+
+def _abi3_compatible(python_tag: str, target_ver: tuple[int, int]) -> bool:
+    """Check if an abi3 wheel's cpXY tag is compatible with *target_ver*."""
+    for sub in python_tag.split("."):
+        if sub.startswith("cp") and len(sub) >= 3:
+            digits = sub[2:]
+            try:
+                tag_major = int(digits[0])
+                tag_minor = int(digits[1:]) if len(digits) > 1 else 0
+                if (tag_major, tag_minor) <= target_ver:
+                    return True
+            except ValueError:
+                pass
+    return False
+
+
+def is_wheel_compatible(
+    python_tag: str,
+    platform_tag: str,
+    target_python: str,
+    target_platforms: Sequence[str],
+    abi_tag: str = "",
+) -> bool:
+    """Check if a wheel's tags match the target environment.
+
+    *target_python* is e.g. ``"3.12"``; *target_platforms* is e.g.
+    ``["linux_x86_64", "linux_aarch64"]``.
+    """
+    major, minor = target_python.split(".")
+    target_ver = (int(major), int(minor))
+    compatible_py = {
+        f"cp{major}{minor}",
+        f"cp{major}",
+        f"py{major}",
+        f"py{major}{minor}",
+    }
+
+    py_ok = any(sub in compatible_py for sub in python_tag.split("."))
+    if not py_ok and abi_tag and "abi3" in abi_tag.split("."):
+        py_ok = _abi3_compatible(python_tag, target_ver)
+    if not py_ok:
+        return False
+
+    # Platform matching: "any" and "none" always match.
+    if platform_tag.lower() in ("any", "none"):
+        return True
+
+    # A compound platform tag like "manylinux_2_17_x86_64.manylinux2014_x86_64"
+    # can contain multiple sub-tags separated by ".". Check each sub-tag and
+    # each target platform for a suffix match (the arch part).
+    sub_tags = platform_tag.split(".")
+    for target in target_platforms:
+        # Extract the arch from the target, e.g. "linux_x86_64" → "x86_64"
+        arch = target.split("_", 1)[1] if "_" in target else target
+        for sub in sub_tags:
+            if sub == target or sub.endswith(f"_{arch}"):
+                return True
+
+    return False
+
+
+# ---------------------------------------------------------------------------
+# Task 6 — RHOAI index loader
+# ---------------------------------------------------------------------------
+
+
+class RhoaiIndex:
+    """RHOAI simple index with lazy per-package fetching.
+
+    The root page is downloaded eagerly (to learn which packages exist),
+    but individual package pages are fetched on-demand and cached.
+    """
+
+    def __init__(
+        self, index_url: str, python_version: str, platforms: Sequence[str]
+    ) -> None:
+        """Initialize with the RHOAI simple index URL, target Python version, and platforms."""
+        self.index_url = index_url.rstrip("/") + "/"
+        self.python_version = python_version
+        self.platforms = list(platforms)
+        self._parser = SimpleIndexParser()
+        self._known_packages: set[str] = set()
+        self._packages: dict[str, dict[str, dict[str, tuple[str, str]]]] = {}
+
+    def _fetch_url(self, url: str) -> str:
+        """Fetch *url* with retry (3 attempts, exponential backoff)."""
+        last_exc: Exception | None = None
+        for attempt in range(3):
+            try:
+                with urllib.request.urlopen(url, timeout=30) as resp:
+                    return str(resp.read().decode())
+            except Exception as exc:
+                last_exc = exc
+                logger.debug("Fetch %s attempt %d failed: %s", url, attempt + 1, exc)
+                time.sleep(2**attempt)
+        raise RuntimeError(f"Failed to fetch {url} after 3 attempts") from last_exc
+
+    def load(self) -> None:
+        """Download root page to learn which packages exist on RHOAI."""
+        root_html = self._fetch_url(self.index_url)
+        package_names = self._parser.parse_root(root_html)
+        self._known_packages = {normalize_name(n) for n in package_names}
+        logger.info("RHOAI index: %d packages available", len(self._known_packages))
+
+    def _ensure_loaded(self, name: str) -> None:
+        """Fetch and cache a package page if not already loaded."""
+        norm = normalize_name(name)
+        if norm in self._packages or norm not in self._known_packages:
+            return
+
+        target_platforms = [f"linux_{p}" for p in self.platforms]
+        page_url = f"{self.index_url}{norm}/"
+        try:
+            page_html = self._fetch_url(page_url)
+        except Exception as exc:
+            logger.warning("Failed to fetch RHOAI page for %s: %s", norm, exc)
+            return
+
+        entries = self._parser.parse_package_page(page_html)
+        versions: dict[str, dict[str, tuple[str, str]]] = {}
+        for entry in entries:
+            if not entry["is_wheel"]:
+                continue
+            if not is_wheel_compatible(
+                entry["python_tag"],
+                entry["platform_tag"],
+                self.python_version,
+                target_platforms,
+                abi_tag=entry.get("abi_tag", ""),
+            ):
+                continue
+
+            ver = entry["version"]
+            if ver not in versions:
+                versions[ver] = {}
+
+            plat = entry["platform_tag"]
+            matched_arch = self._match_arch(plat, target_platforms)
+            if matched_arch:
+                versions[ver][matched_arch] = (entry["filename"], entry["sha256"])
+
+        if versions:
+            self._packages[norm] = versions
+
+    def _match_arch(self, platform_tag: str, target_platforms: list[str]) -> str | None:
+        """Determine which target arch a platform tag matches."""
+        if platform_tag.lower() in ("any", "none"):
+            return "any"
+        sub_tags = platform_tag.split(".")
+        for target in target_platforms:
+            arch = target.split("_", 1)[1] if "_" in target else target
+            for sub in sub_tags:
+                if sub == target or sub.endswith(f"_{arch}"):
+                    return target
+        return None
+
+    def has_package(self, name: str) -> bool:
+        """Return whether the RHOAI index lists this package name."""
+        return normalize_name(name) in self._known_packages
+
+    def find_best(self, name: str, constraint: str) -> dict[str, Any] | None:
+        """Find latest version satisfying *constraint*.
+
+        Returns ``{"version": str, "platforms": {arch: (filename, sha256)}}``
+        or ``None``.
+        """
+        norm = normalize_name(name)
+        self._ensure_loaded(norm)
+        versions = self._packages.get(norm)
+        if not versions:
+            return None
+
+        candidates = [v for v in versions if version_satisfies(v, constraint)]
+        if not candidates:
+            return None
+
+        best = max(candidates, key=parse_version)
+        return {"version": best, "platforms": versions[best]}
+
+
+# ---------------------------------------------------------------------------
+# Task 7 — PEP 508 marker evaluation & PyPI client
+# ---------------------------------------------------------------------------
+
+_MARKER_ENV_KEYS = {
+    "sys_platform",
+    "os_name",
+    "platform_system",
+    "implementation_name",
+    "python_version",
+    "platform_machine",
+    "extra",
+}
+
+_MARKER_COMPARE_RE = re.compile(
+    r"""^
+    \s*(?P[A-Za-z_][A-Za-z0-9_.]*|'[^']*'|"[^"]*")
+    \s*(?P~=|===|==|!=|>=|<=|>|<|not\s+in|in)
+    \s*(?P[A-Za-z_][A-Za-z0-9_.]*|'[^']*'|"[^"]*")
+    \s*$
+    """,
+    re.VERBOSE,
+)
+
+
+def _eval_marker(marker: str, python_version: str) -> bool:
+    """Simplified PEP 508 marker evaluation for a Linux CPython target."""
+    env = {
+        "sys_platform": "linux",
+        "os_name": "posix",
+        "platform_system": "Linux",
+        "implementation_name": "cpython",
+        "python_version": python_version,
+    }
+
+    marker = marker.strip()
+    if not marker:
+        return True
+
+    or_parts = re.split(r"\s+or\s+", marker)
+    for or_part in or_parts:
+        and_parts = re.split(r"\s+and\s+", or_part)
+        all_true = True
+        for expr in and_parts:
+            if not _eval_single_marker(expr.strip(), env):
+                all_true = False
+                break
+        if all_true:
+            return True
+    return False
+
+
+_MARKER_CMP_OPS: dict[str, Any] = {
+    "==": lambda lv, rv: lv == rv,
+    "!=": lambda lv, rv: lv != rv,
+    ">=": lambda lv, rv: lv >= rv,
+    "<=": lambda lv, rv: lv <= rv,
+    ">": lambda lv, rv: lv > rv,
+    "<": lambda lv, rv: lv < rv,
+    "in": lambda lv, rv: lv in rv,
+    "not in": lambda lv, rv: lv not in rv,
+}
+
+
+def _eval_single_marker(expr: str, env: dict[str, str]) -> bool:
+    """Evaluate a single marker comparison like ``sys_platform == 'linux'``."""
+    m = _MARKER_COMPARE_RE.match(expr)
+    if m is None:
+        return True
+
+    left_raw = m.group("left").strip("'\"")
+    right_raw = m.group("right").strip("'\"")
+    op = re.sub(r"\s+", " ", m.group("op"))
+
+    if left_raw in env:
+        lval, rval = env[left_raw], right_raw
+    elif right_raw in env:
+        lval, rval = left_raw, env[right_raw]
+    else:
+        return True
+
+    check = _MARKER_CMP_OPS.get(op)
+    return bool(check(lval, rval)) if check else True
+
+
+class PypiClient:
+    """Lazy, per-package PyPI client with caching."""
+
+    def __init__(self, python_version: str, platforms: Sequence[str]) -> None:
+        """Initialize with the target Python version and platforms."""
+        self.python_version = python_version
+        self.platforms = list(platforms)
+        self._parser = SimpleIndexParser()
+        self._info_cache: dict[str, dict[str, Any]] = {}
+        self._requires_cache: dict[str, list[tuple[str, str]]] = {}
+
+    def _fetch_url(self, url: str) -> str:
+        """Fetch *url* with retry (3 attempts, exponential backoff)."""
+        last_exc: Exception | None = None
+        for attempt in range(3):
+            try:
+                with urllib.request.urlopen(url, timeout=30) as resp:
+                    return str(resp.read().decode())
+            except Exception as exc:
+                last_exc = exc
+                logger.debug("Fetch %s attempt %d failed: %s", url, attempt + 1, exc)
+                time.sleep(2**attempt)
+        raise RuntimeError(f"Failed to fetch {url} after 3 attempts") from last_exc
+
+    def get_package_info(self, name: str) -> dict[str, dict[str, Any]]:
+        """Fetch and cache the simple index page for *name*.
+
+        Returns ``{version: {"has_sdist": bool, "sdist_hashes": [...],
+        "wheel_hashes": [...], "wheel_files": [...]}}``.
+        """
+        norm = normalize_name(name)
+        if norm in self._info_cache:
+            return self._info_cache[norm]
+
+        url = f"https://pypi.org/simple/{norm}/"
+        html = self._fetch_url(url)
+        entries = self._parser.parse_package_page(html)
+
+        info: dict[str, dict[str, Any]] = {}
+        for entry in entries:
+            ver = entry["version"]
+            if ver not in info:
+                info[ver] = {
+                    "has_sdist": False,
+                    "sdist_hashes": [],
+                    "wheel_hashes": [],
+                    "wheel_files": [],
+                }
+            if entry["is_wheel"]:
+                if entry["sha256"]:
+                    info[ver]["wheel_hashes"].append(entry["sha256"])
+                info[ver]["wheel_files"].append(entry["filename"])
+            else:
+                info[ver]["has_sdist"] = True
+                if entry["sha256"]:
+                    info[ver]["sdist_hashes"].append(entry["sha256"])
+
+        self._info_cache[norm] = info
+        return info
+
+    def get_requires_dist(self, name: str, version: str) -> list[tuple[str, str]]:
+        """Fetch ``Requires-Dist`` from PyPI JSON API.
+
+        Returns ``[(dep_name, spec), ...]``, filtering out extras and markers
+        that don't match the target environment.
+        """
+        cache_key = f"{normalize_name(name)}=={version}"
+        if cache_key in self._requires_cache:
+            return self._requires_cache[cache_key]
+
+        url = f"https://pypi.org/pypi/{name}/{version}/json"
+        text = self._fetch_url(url)
+        data = json.loads(text)
+
+        requires_dist: list[str] = data.get("info", {}).get("requires_dist") or []
+        result: list[tuple[str, str]] = []
+
+        for dep_str in requires_dist:
+            dep_name, spec, marker = _parse_dep_string(dep_str)
+
+            if marker:
+                stripped = marker.replace(" ", "")
+                if "extra==" in stripped or "extra ==" in marker:
+                    continue
+
+            if marker and not _eval_marker(marker, self.python_version):
+                continue
+
+            result.append((dep_name, spec))
+
+        self._requires_cache[cache_key] = result
+        return result
+
+    def find_best(self, name: str, constraint: str) -> dict[str, Any] | None:
+        """Find latest version on PyPI satisfying *constraint*.
+
+        Returns ``{"version": str, "has_sdist": bool, "sdist_hashes": [...],
+        "wheel_hashes": [...], "wheel_files": [...]}`` or ``None``.
+        """
+        info = self.get_package_info(name)
+        candidates = [v for v in info if version_satisfies(v, constraint)]
+        if not candidates:
+            return None
+
+        best = max(candidates, key=parse_version)
+        return {"version": best, **info[best]}
+
+
+# ---------------------------------------------------------------------------
+# Task 8 — Dependency resolver (BFS graph walk)
+# ---------------------------------------------------------------------------
+
+
+class Resolver:
+    """BFS dependency resolver enforcing RHOAI-first policy."""
+
+    def __init__(
+        self,
+        rhoai: RhoaiIndex,
+        pypi: PypiClient,
+        wheel_only_packages: set[str] | None = None,
+    ) -> None:
+        """Initialize with RHOAI and PyPI clients and the wheel-only package set."""
+        self.rhoai = rhoai
+        self.pypi = pypi
+        self.wheel_only = {normalize_name(p) for p in (wheel_only_packages or set())}
+        self.fallback_reasons: dict[str, str] = {}
+
+    def resolve(self, direct_deps: list[tuple[str, str]]) -> dict[str, dict[str, Any]]:
+        """Resolve all transitive dependencies via BFS.
+
+        Returns ``{name: {"version": str, "source": "rhoai"|"pypi", ...}}``.
+        """
+        resolved: dict[str, dict[str, Any]] = {}
+        constraints: dict[str, str] = {}
+        queue: deque[tuple[str, str]] = deque()
+
+        for name, spec in direct_deps:
+            norm = normalize_name(name)
+            constraints[norm] = (
+                merge_constraints(constraints.get(norm), spec)
+                if spec
+                else constraints.get(norm, "")
+            )
+            queue.append((norm, constraints[norm]))
+
+        visited_queue: set[str] = set()
+
+        while queue:
+            name, _constraint_at_enqueue = queue.popleft()
+            norm = normalize_name(name)
+
+            if norm in resolved:
+                current_ver = resolved[norm]["version"]
+                if version_satisfies(current_ver, constraints.get(norm, "")):
+                    continue
+                if resolved[norm]["source"] == "rhoai":
+                    logger.info(
+                        "Constraint conflict for %s (RHOAI %s); falling back to PyPI",
+                        norm,
+                        current_ver,
+                    )
+                    del resolved[norm]
+                    self.fallback_reasons[norm] = (
+                        f"RHOAI version {current_ver} conflicts with "
+                        f"constraint {constraints.get(norm, '')}"
+                    )
+                else:
+                    raise RuntimeError(
+                        f"Constraint conflict for {norm}: resolved {current_ver} "
+                        f"does not satisfy {constraints.get(norm, '')}"
+                    )
+
+            constraint = constraints.get(norm, "")
+
+            rhoai_result = self.rhoai.find_best(norm, constraint)
+            if rhoai_result is not None:
+                resolved[norm] = {
+                    "version": rhoai_result["version"],
+                    "source": "rhoai",
+                    "platforms": rhoai_result["platforms"],
+                }
+            else:
+                pypi_result = self.pypi.find_best(norm, constraint)
+                if pypi_result is None:
+                    raise RuntimeError(
+                        f"Cannot resolve {norm} with constraint "
+                        f"{constraint!r}: not found on RHOAI or PyPI"
+                    )
+                if norm not in self.fallback_reasons:
+                    if self.rhoai.has_package(norm):
+                        self.fallback_reasons[norm] = (
+                            f"RHOAI has {norm} but no version satisfies {constraint!r}"
+                        )
+                    else:
+                        self.fallback_reasons[norm] = "not in RHOAI index"
+                resolved[norm] = {
+                    "version": pypi_result["version"],
+                    "source": "pypi",
+                    "has_sdist": pypi_result["has_sdist"],
+                    "sdist_hashes": pypi_result["sdist_hashes"],
+                    "wheel_hashes": pypi_result["wheel_hashes"],
+                    "wheel_files": pypi_result["wheel_files"],
+                }
+
+            pinned_version = resolved[norm]["version"]
+            visit_key = f"{norm}=={pinned_version}"
+            if visit_key in visited_queue:
+                continue
+            visited_queue.add(visit_key)
+
+            try:
+                trans_deps = self.pypi.get_requires_dist(norm, pinned_version)
+            except Exception as exc:
+                logger.warning(
+                    "Could not fetch deps for %s==%s: %s", norm, pinned_version, exc
+                )
+                continue
+
+            for dep_name, dep_spec in trans_deps:
+                dep_norm = normalize_name(dep_name)
+                if dep_spec:
+                    constraints[dep_norm] = merge_constraints(
+                        constraints.get(dep_norm), dep_spec
+                    )
+                elif dep_norm not in constraints:
+                    constraints[dep_norm] = ""
+                queue.append((dep_norm, constraints[dep_norm]))
+
+        return resolved
+
+
+# ---------------------------------------------------------------------------
+# Task 9 — Classifier
+# ---------------------------------------------------------------------------
+
+
+def classify_packages(
+    resolved: dict[str, dict[str, Any]],
+    wheel_only: set[str],
+) -> dict[str, dict[str, dict[str, Any]]]:
+    """Classify resolved packages into output buckets.
+
+    Returns ``{"rhoai_wheel": {...}, "pypi_sdist": {...}, "pypi_wheel": {...}}``.
+    """
+    wheel_only_norm = {normalize_name(p) for p in wheel_only}
+
+    buckets: dict[str, dict[str, dict[str, Any]]] = {
+        "rhoai_wheel": {},
+        "pypi_sdist": {},
+        "pypi_wheel": {},
+    }
+
+    for name, info in resolved.items():
+        norm = normalize_name(name)
+        if info["source"] == "rhoai":
+            buckets["rhoai_wheel"][norm] = info
+        elif norm in wheel_only_norm:
+            buckets["pypi_wheel"][norm] = info
+        elif info.get("has_sdist", False):
+            buckets["pypi_sdist"][norm] = info
+        else:
+            logger.warning(
+                "Package %s==%s has no sdist on PyPI; auto-promoting to "
+                "pypi_wheel. Consider adding it to pypi_wheel_only.txt.",
+                norm,
+                info["version"],
+            )
+            buckets["pypi_wheel"][norm] = info
+
+    return buckets
+
+
+# ---------------------------------------------------------------------------
+# Task 10 — Output writer: hashed requirements files
+# ---------------------------------------------------------------------------
+
+
+def write_hashed_requirements(
+    packages: dict[str, dict[str, Any]],
+    output_path: str,
+    index_url: str,
+) -> None:
+    """Write a pip-compatible hashed requirements file.
+
+    *packages* maps ``{name: info}`` where *info* has the fields produced by
+    the resolver (``version``, and either ``platforms`` for RHOAI or
+    ``sdist_hashes`` / ``wheel_hashes`` / ``wheel_files`` for PyPI).
+    """
+    lines: list[str] = [f"--index-url {index_url}\n"]
+
+    for name in sorted(packages):
+        info = packages[name]
+        version = info["version"]
+
+        hashes: set[str] = set()
+
+        # RHOAI packages store hashes per platform
+        if "platforms" in info:
+            for _arch, (_, sha) in info["platforms"].items():
+                if sha:
+                    hashes.add(sha)
+
+        # PyPI packages store hashes in flat lists
+        for key in ("sdist_hashes", "wheel_hashes"):
+            for sha in info.get(key, []):
+                if sha:
+                    hashes.add(sha)
+        # wheel_files entries are filenames, not hashes — but the info dict
+        # may carry per-file hashes via wheel_hashes already.  Nothing extra
+        # to extract here.
+
+        sorted_hashes = sorted(hashes)
+        if not sorted_hashes:
+            raise RuntimeError(
+                f"No hashes collected for {name}=={version} while writing {output_path}"
+            )
+        lines.append(f"{name}=={version} \\\n")
+        hash_lines = [f"    --hash=sha256:{h}" for h in sorted_hashes]
+        lines.append(" \\\n".join(hash_lines) + "\n")
+
+    with open(output_path, "w") as f:
+        f.writelines(lines)
+
+
+# ---------------------------------------------------------------------------
+# Task 11 — Tekton YAML patching
+# ---------------------------------------------------------------------------
+
+
+def patch_tekton_packages(yaml_path: str, package_names: list[str]) -> None:
+    """Replace the ``"packages": "..."`` value in a Tekton pipeline YAML."""
+    with open(yaml_path) as f:
+        content = f.read()
+
+    sorted_names = sorted(package_names)
+    replacement = f'"packages": "{",".join(sorted_names)}"'
+    content = re.sub(r'"packages":\s*"[^"]*"', replacement, content)
+
+    with open(yaml_path, "w") as f:
+        f.write(content)
+
+
+# ---------------------------------------------------------------------------
+# Task 12 — Config loading
+# ---------------------------------------------------------------------------
+
+KONFLUX_DIR = ".konflux"
+
+
+def load_config(profiles_path: str, profile_name: str) -> dict[str, Any]:
+    """Load and merge ``[common]`` + ``[profiles.]`` from a TOML file.
+
+    Returns a dict with keys: ``python_version``, ``platforms``,
+    ``bootstrap_packages``, ``rhoai_index_url``, ``output_suffix``,
+    ``tekton_files``.
+    """
+    with open(profiles_path, "rb") as f:
+        data = tomllib.load(f)
+
+    common = dict(data.get("common", {}))
+    profiles = data.get("profiles", {})
+
+    if profile_name not in profiles:
+        raise KeyError(
+            f"Profile {profile_name!r} not found in {profiles_path}. "
+            f"Available: {', '.join(profiles)}"
+        )
+
+    merged = {**common, **profiles[profile_name]}
+    return merged
+
+
+def load_wheel_only(path: str) -> set[str]:
+    """Load ``.konflux/pypi_wheel_only.txt`` — one package name per line.
+
+    Skips blank lines and ``#`` comments.  Returns normalized names.
+    """
+    names: set[str] = set()
+    with open(path) as f:
+        for line in f:
+            line = line.strip()
+            if not line or line.startswith("#"):
+                continue
+            names.add(normalize_name(line))
+    return names
+
+
+# ---------------------------------------------------------------------------
+# Hybrid resolution: uv pip compile + RHOAI reclassification
+# ---------------------------------------------------------------------------
+
+_UV_COMPILED_RE = re.compile(r"^([a-zA-Z0-9][a-zA-Z0-9._-]*)([=<>!~].*)?$")
+
+
+UV_BINARY = os.environ.get(
+    "UV_BINARY",
+    os.path.join(
+        os.path.dirname(__file__), "..", "..", "uv", "target", "release", "uv"
+    ),
+)
+
+
+def uv_resolve(
+    python_version: str,
+    rhoai_index_url: str,
+    suffix: str,
+) -> dict[str, dict[str, Any]]:
+    """Run ``uv pip compile --index-strategy prefer-index`` to resolve deps.
+
+    Returns ``{normalized_name: {"version": str, "index": str}}``
+    where *index* is the URL of the index the package was resolved from.
+    """
+    overrides_file = os.path.join(
+        KONFLUX_DIR,
+        (
+            f"requirements.overrides{suffix}.txt"
+            if suffix
+            else "requirements.overrides.txt"
+        ),
+    )
+    uv = UV_BINARY if os.path.isfile(UV_BINARY) else "uv"
+    cmd = [
+        uv,
+        "pip",
+        "compile",
+        "pyproject.toml",
+        "--python-platform",
+        "x86_64-manylinux_2_28",
+        "--python-version",
+        python_version,
+        "--refresh",
+        "--index",
+        rhoai_index_url,
+        "--default-index",
+        "https://pypi.org/simple/",
+        "--index-strategy",
+        "prefer-index",
+        "--emit-index-annotation",
+        "--no-sources",
+        "--group",
+        "llslibdev",
+    ]
+    if os.path.exists(overrides_file):
+        cmd += ["--override", overrides_file]
+
+    logger.debug("Running: %s", " ".join(cmd))
+    result = subprocess.run(cmd, capture_output=True, text=True, check=True)
+
+    resolved: dict[str, dict[str, Any]] = {}
+    current_package: str | None = None
+
+    for line in result.stdout.splitlines():
+        line = line.strip()
+        if not line or line.startswith("-"):
+            continue
+
+        m = _UV_COMPILED_RE.match(line)
+        if m and not line.startswith("#"):
+            name = normalize_name(m.group(1))
+            version_spec = (m.group(2) or "").strip()
+            if version_spec.startswith("=="):
+                version = version_spec[2:]
+            else:
+                version = version_spec.lstrip("=")
+            if version:
+                current_package = name
+                resolved[name] = {"version": version, "index": ""}
+        elif "# from " in line and current_package:
+            index_url = line.split("# from ", 1)[1].strip()
+            resolved[current_package]["index"] = index_url
+
+    logger.info("uv resolved %d packages", len(resolved))
+    return resolved
+
+
+def reclassify_with_rhoai(
+    uv_resolved: dict[str, str],
+    rhoai: RhoaiIndex,
+) -> dict[str, dict[str, Any]]:
+    """Reclassify uv-resolved packages using RHOAI-first policy.
+
+    For each package, if RHOAI has a compatible wheel at the resolved version,
+    classify as ``source=rhoai``; otherwise ``source=pypi``.
+    """
+    result: dict[str, dict[str, Any]] = {}
+    rhoai_count = 0
+    pypi_count = 0
+
+    for name, version in sorted(uv_resolved.items()):
+        rhoai_match = rhoai.find_best(name, f"=={version}")
+        if rhoai_match and rhoai_match["version"] == version:
+            result[name] = {
+                "version": version,
+                "source": "rhoai",
+                "platforms": rhoai_match["platforms"],
+            }
+            rhoai_count += 1
+            logger.debug("RHOAI: %s==%s", name, version)
+        else:
+            result[name] = {
+                "version": version,
+                "source": "pypi",
+                "has_sdist": True,
+                "sdist_hashes": [],
+                "wheel_hashes": [],
+                "wheel_files": [],
+            }
+            pypi_count += 1
+            if rhoai.has_package(name):
+                logger.info(
+                    "PyPI: %s==%s (RHOAI has package but not version %s)",
+                    name,
+                    version,
+                    version,
+                )
+            else:
+                logger.debug("PyPI: %s==%s (not in RHOAI)", name, version)
+
+    logger.info("Reclassified: %d RHOAI, %d PyPI", rhoai_count, pypi_count)
+    return result
+
+
+def _fetch_hashes_for_pypi_packages(
+    resolved: dict[str, dict[str, Any]],
+    pypi: PypiClient,
+) -> None:
+    """Populate sdist/wheel hashes for PyPI-sourced packages in-place."""
+    for name, info in resolved.items():
+        if info["source"] != "pypi":
+            continue
+        version = info["version"]
+        try:
+            pkg_info = pypi.get_package_info(name)
+        except Exception as exc:
+            logger.warning("Could not fetch PyPI info for %s: %s", name, exc)
+            continue
+        ver_info = pkg_info.get(version, {})
+        info["has_sdist"] = ver_info.get("has_sdist", False)
+        info["sdist_hashes"] = ver_info.get("sdist_hashes", [])
+        info["wheel_hashes"] = ver_info.get("wheel_hashes", [])
+        info["wheel_files"] = ver_info.get("wheel_files", [])
+
+
+# ---------------------------------------------------------------------------
+# Main entrypoint
+# ---------------------------------------------------------------------------
+
+
+def _strip_rhoai_duplicates_from_build_deps(
+    build_file: str, rhoai_names: set[str]
+) -> None:
+    """Remove packages from build deps file that already exist as RHOAI wheels.
+
+    Prevents hermeto from fetching PyPI wheels for packages that are already
+    available from RHOAI, which would cause EC policy violations (binary=true
+    on PyPI-sourced packages).
+    """
+    with open(build_file) as f:
+        lines = f.readlines()
+
+    output: list[str] = []
+    skip = False
+    for line in lines:
+        if line and line[0].isalpha():
+            pkg_name = normalize_name(line.split("==")[0].strip())
+            skip = pkg_name in rhoai_names
+        elif line.startswith("#") and not skip:
+            skip = False
+
+        if not skip:
+            output.append(line)
+
+    with open(build_file, "w") as f:
+        f.writelines(output)
+
+
+def main() -> None:
+    """Resolve dependencies with RHOAI-first policy and write Hermeto output files."""
+    parser = argparse.ArgumentParser(
+        description="Policy-driven dependency resolver for Hermeto/Cachi2 builds."
+    )
+    parser.add_argument("--profile", required=True, help="Build profile (cpu|cuda)")
+    verbosity = parser.add_mutually_exclusive_group()
+    verbosity.add_argument("--verbose", action="store_true", help="Verbose logging")
+    verbosity.add_argument("--quiet", action="store_true", help="Errors only")
+    args = parser.parse_args()
+
+    if args.verbose:
+        logging.basicConfig(level=logging.DEBUG)
+    elif args.quiet:
+        logging.basicConfig(level=logging.ERROR)
+    else:
+        logging.basicConfig(level=logging.INFO)
+
+    profiles_path = os.path.join(KONFLUX_DIR, "profiles.toml")
+    config = load_config(profiles_path, args.profile)
+
+    wheel_only_path = os.path.join(KONFLUX_DIR, "pypi_wheel_only.txt")
+    wheel_only = load_wheel_only(wheel_only_path)
+
+    python_version = config["python_version"]
+    platforms = config["platforms"]
+    rhoai_index_url = config["rhoai_index_url"]
+    suffix = config.get("output_suffix", "")
+    tekton_files = config.get("tekton_files", [])
+    bootstrap_packages = config.get("bootstrap_packages", [])
+
+    # Step 1: Resolve via uv with prefer-index strategy
+    logger.info("Running uv pip compile --index-strategy prefer-index …")
+    uv_resolved = uv_resolve(python_version, rhoai_index_url, suffix)
+
+    # Step 2: Build resolved dict from uv output + index annotations
+    resolved: dict[str, dict[str, Any]] = {}
+    for name, info in uv_resolved.items():
+        index = info["index"]
+        is_rhoai = "packages.redhat.com" in index if index else False
+        if is_rhoai:
+            resolved[name] = {
+                "version": info["version"],
+                "source": "rhoai",
+                "platforms": {},
+            }
+        else:
+            resolved[name] = {
+                "version": info["version"],
+                "source": "pypi",
+                "has_sdist": True,
+                "sdist_hashes": [],
+                "wheel_hashes": [],
+                "wheel_files": [],
+            }
+
+    rhoai_count = sum(1 for v in resolved.values() if v["source"] == "rhoai")
+    pypi_count = len(resolved) - rhoai_count
+    logger.info("Classified: %d RHOAI, %d PyPI", rhoai_count, pypi_count)
+
+    # Step 3: Fetch hashes — RHOAI from the RHOAI index, PyPI from PyPI
+    logger.info("Fetching hashes …")
+    rhoai = RhoaiIndex(rhoai_index_url, python_version, platforms)
+    rhoai.load()
+    for name, info in resolved.items():
+        if info["source"] == "rhoai":
+            match = rhoai.find_best(name, f"=={info['version']}")
+            if match:
+                info["platforms"] = match["platforms"]
+
+    pypi = PypiClient(python_version, platforms)
+    _fetch_hashes_for_pypi_packages(resolved, pypi)
+
+    # Step 5: Classify into buckets
+    buckets = classify_packages(resolved, wheel_only)
+
+    # Step 6: Write hashed requirements files
+    write_hashed_requirements(
+        buckets["rhoai_wheel"],
+        os.path.join(KONFLUX_DIR, f"requirements.hashes.wheel{suffix}.txt"),
+        rhoai_index_url,
+    )
+    write_hashed_requirements(
+        buckets["pypi_sdist"],
+        os.path.join(KONFLUX_DIR, f"requirements.hashes.source{suffix}.txt"),
+        "https://pypi.org/simple",
+    )
+    write_hashed_requirements(
+        buckets["pypi_wheel"],
+        os.path.join(KONFLUX_DIR, f"requirements.hashes.wheel.pypi{suffix}.txt"),
+        "https://pypi.org/simple",
+    )
+
+    # Step 7: Build dependencies via pybuild-deps
+    sdist_names = list(buckets["pypi_sdist"].keys())
+    build_output = os.path.join(KONFLUX_DIR, f"requirements-build{suffix}.txt")
+    if sdist_names:
+        tmp_sdist_file = os.path.join(KONFLUX_DIR, f"_tmp_sdist_list{suffix}.txt")
+        try:
+            with open(tmp_sdist_file, "w") as f:
+                for name in sorted(sdist_names):
+                    info = buckets["pypi_sdist"][name]
+                    f.write(f"{name}=={info['version']}\n")
+            subprocess.run(
+                [
+                    "uv",
+                    "run",
+                    "pybuild-deps",
+                    "compile",
+                    f"--output-file={build_output}",
+                    tmp_sdist_file,
+                ],
+                check=True,
+            )
+        finally:
+            if os.path.exists(tmp_sdist_file):
+                os.remove(tmp_sdist_file)
+
+        # Strip build deps that duplicate RHOAI wheel packages to avoid
+        # hermeto fetching them as binary from PyPI (EC policy violation).
+        rhoai_names = set(buckets["rhoai_wheel"].keys())
+        _strip_rhoai_duplicates_from_build_deps(build_output, rhoai_names)
+    else:
+        with open(build_output, "w") as f:
+            f.write("# No sdist packages — no build dependencies needed.\n")
+
+    # Step 9: Patch Tekton pipelines
+    wheel_package_names = (
+        list(buckets["rhoai_wheel"].keys())
+        + list(buckets["pypi_wheel"].keys())
+        + [normalize_name(p) for p in bootstrap_packages]
+    )
+    for tekton_file in tekton_files:
+        if os.path.exists(tekton_file):
+            patch_tekton_packages(tekton_file, wheel_package_names)
+            logger.info("Patched %s", tekton_file)
+        else:
+            logger.warning("Tekton file not found: %s", tekton_file)
+
+    # Summary
+    total = len(resolved)
+    print(f"\n{'='*60}")
+    print(f"Resolution complete ({args.profile} profile)")
+    print(f"{'='*60}")
+    print(f"  RHOAI wheels:          {len(buckets['rhoai_wheel']):>4} packages")
+    print(f"  PyPI sdist:            {len(buckets['pypi_sdist']):>4} packages")
+    print(f"  PyPI wheel (last resort): {len(buckets['pypi_wheel']):>4} packages")
+    print(f"  Total:                 {total:>4} packages")
+    print()
+    print(f"  Hashed wheel (RHOAI):  .konflux/requirements.hashes.wheel{suffix}.txt")
+    print(f"  Hashed source (PyPI):  .konflux/requirements.hashes.source{suffix}.txt")
+    print(
+        f"  Hashed wheel (PyPI):   .konflux/requirements.hashes.wheel.pypi{suffix}.txt"
+    )
+    print(f"  Build deps:            {build_output}")
+    print()
+    print("Remember to commit output files and push the changes.")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/src/app/endpoints/vector_stores.py b/src/app/endpoints/vector_stores.py
new file mode 100644
index 000000000..91892f520
--- /dev/null
+++ b/src/app/endpoints/vector_stores.py
@@ -0,0 +1,840 @@
+"""Handler for REST API calls to manage vector stores and files."""
+
+import asyncio
+import os
+from io import BytesIO
+from typing import Annotated, Any
+
+from fastapi import APIRouter, Depends, File, HTTPException, Request, UploadFile, status
+from llama_stack_client import (
+    APIConnectionError,
+    BadRequestError,
+)
+from llama_stack_client import (
+    APIStatusError as LLSApiStatusError,
+)
+from openai._exceptions import APIStatusError as OpenAIAPIStatusError
+
+from authentication import get_auth_dependency
+from authentication.interface import AuthTuple
+from authorization.middleware import authorize
+from client import AsyncLlamaStackClientHolder
+from configuration import configuration
+from constants import DEFAULT_MAX_FILE_UPLOAD_SIZE
+from log import get_logger
+from models.config import Action
+from models.requests import (
+    VectorStoreCreateRequest,
+    VectorStoreFileCreateRequest,
+    VectorStoreUpdateRequest,
+)
+from models.responses import (
+    BadRequestResponse,
+    FileResponse,
+    FileTooLargeResponse,
+    ForbiddenResponse,
+    InternalServerErrorResponse,
+    NotFoundResponse,
+    ServiceUnavailableResponse,
+    UnauthorizedResponse,
+    VectorStoreFileResponse,
+    VectorStoreFilesListResponse,
+    VectorStoreResponse,
+    VectorStoresListResponse,
+)
+from utils.endpoints import check_configuration_loaded
+from utils.query import handle_known_apistatus_errors
+
+logger = get_logger(__name__)
+router = APIRouter(tags=["vector-stores"])
+
+
+# Response schemas for OpenAPI documentation
+vector_stores_list_responses: dict[int | str, dict[str, Any]] = {
+    200: VectorStoresListResponse.openapi_response(),
+    401: UnauthorizedResponse.openapi_response(
+        examples=["missing header", "missing token"]
+    ),
+    403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
+    500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
+    503: ServiceUnavailableResponse.openapi_response(),
+}
+
+vector_store_responses: dict[int | str, dict[str, Any]] = {
+    200: VectorStoreResponse.openapi_response(),
+    401: UnauthorizedResponse.openapi_response(
+        examples=["missing header", "missing token"]
+    ),
+    403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
+    404: NotFoundResponse.openapi_response(examples=["vector store"]),
+    500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
+    503: ServiceUnavailableResponse.openapi_response(),
+}
+
+file_responses: dict[int | str, dict[str, Any]] = {
+    200: FileResponse.openapi_response(),
+    400: BadRequestResponse.openapi_response(examples=["file_upload"]),
+    413: FileTooLargeResponse.openapi_response(),
+    401: UnauthorizedResponse.openapi_response(
+        examples=["missing header", "missing token"]
+    ),
+    403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
+    500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
+    503: ServiceUnavailableResponse.openapi_response(),
+}
+
+vector_store_file_responses: dict[int | str, dict[str, Any]] = {
+    200: VectorStoreFileResponse.openapi_response(),
+    401: UnauthorizedResponse.openapi_response(
+        examples=["missing header", "missing token"]
+    ),
+    403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
+    404: NotFoundResponse.openapi_response(examples=["file"]),
+    500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
+    503: ServiceUnavailableResponse.openapi_response(),
+}
+
+vector_store_files_list_responses: dict[int | str, dict[str, Any]] = {
+    200: VectorStoreFilesListResponse.openapi_response(),
+    401: UnauthorizedResponse.openapi_response(
+        examples=["missing header", "missing token"]
+    ),
+    403: ForbiddenResponse.openapi_response(examples=["endpoint"]),
+    404: NotFoundResponse.openapi_response(examples=["vector store"]),
+    500: InternalServerErrorResponse.openapi_response(examples=["configuration"]),
+    503: ServiceUnavailableResponse.openapi_response(),
+}
+
+
+@router.post("/vector-stores", responses=vector_store_responses)
+@authorize(Action.MANAGE_VECTOR_STORES)
+async def create_vector_store(
+    request: Request,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+    body: VectorStoreCreateRequest,
+) -> VectorStoreResponse:
+    """Create a new vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        auth: Authentication tuple from the auth dependency.
+        body: Vector store creation parameters.
+
+    Returns:
+        VectorStoreResponse: The created vector store object.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+
+        # Extract provider_id for extra_body (not a direct client parameter)
+        body_dict = body.model_dump(exclude_none=True)
+        extra_body = {}
+        if "provider_id" in body_dict:
+            extra_body["provider_id"] = body_dict.pop("provider_id")
+        if "embedding_model" in body_dict:
+            extra_body["embedding_model"] = body_dict.pop("embedding_model")
+        if "embedding_dimension" in body_dict:
+            extra_body["embedding_dimension"] = body_dict.pop("embedding_dimension")
+
+        logger.debug(
+            "Creating vector store - body_dict: %s, extra_body: %s",
+            body_dict,
+            extra_body,
+        )
+
+        vector_store = await client.vector_stores.create(
+            **body_dict,
+            extra_body=extra_body,
+        )
+
+        return VectorStoreResponse(
+            id=vector_store.id,
+            name=vector_store.name,
+            created_at=vector_store.created_at,
+            last_active_at=vector_store.last_active_at,
+            expires_at=vector_store.expires_at,
+            status=vector_store.status or "unknown",
+            usage_bytes=vector_store.usage_bytes or 0,
+            metadata=vector_store.metadata,
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while creating vector store: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.get("/vector-stores", responses=vector_stores_list_responses)
+@authorize(Action.READ_VECTOR_STORES)
+async def list_vector_stores(
+    request: Request,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+) -> VectorStoresListResponse:
+    """List all vector stores.
+
+    Parameters:
+        request: The incoming HTTP request.
+        auth: Authentication tuple from the auth dependency.
+
+    Returns:
+        VectorStoresListResponse: List of all vector stores.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        vector_stores = await client.vector_stores.list()
+
+        data = [
+            VectorStoreResponse(
+                id=vs.id,
+                name=vs.name,
+                created_at=vs.created_at,
+                last_active_at=vs.last_active_at,
+                expires_at=vs.expires_at or None,
+                status=vs.status or "unknown",
+                usage_bytes=vs.usage_bytes or 0,
+                metadata=vs.metadata,
+            )
+            for vs in vector_stores.data
+        ]
+
+        return VectorStoresListResponse(data=data)
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while listing vector stores: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.get("/vector-stores/{vector_store_id}", responses=vector_store_responses)
+@authorize(Action.READ_VECTOR_STORES)
+async def get_vector_store(
+    request: Request,
+    vector_store_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+) -> VectorStoreResponse:
+    """Retrieve a vector store by ID.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store to retrieve.
+        auth: Authentication tuple from the auth dependency.
+
+    Returns:
+        VectorStoreResponse: The vector store object.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: Vector store not found
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        vector_store = await client.vector_stores.retrieve(vector_store_id)
+
+        return VectorStoreResponse(
+            id=vector_store.id,
+            name=vector_store.name,
+            created_at=vector_store.created_at,
+            last_active_at=vector_store.last_active_at,
+            expires_at=vector_store.expires_at,
+            status=vector_store.status or "unknown",
+            usage_bytes=vector_store.usage_bytes or 0,
+            metadata=vector_store.metadata,
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store not found: %s", e)
+        response = NotFoundResponse(
+            resource="vector_store", resource_id=vector_store_id
+        )
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while getting vector store: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.put("/vector-stores/{vector_store_id}", responses=vector_store_responses)
+@authorize(Action.MANAGE_VECTOR_STORES)
+async def update_vector_store(
+    request: Request,
+    vector_store_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+    body: VectorStoreUpdateRequest,
+) -> VectorStoreResponse:
+    """Update a vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store to update.
+        auth: Authentication tuple from the auth dependency.
+        body: Vector store update parameters.
+
+    Returns:
+        VectorStoreResponse: The updated vector store object.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: Vector store not found
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        vector_store = await client.vector_stores.update(
+            vector_store_id, **body.model_dump(exclude_none=True)
+        )
+
+        return VectorStoreResponse(
+            id=vector_store.id,
+            name=vector_store.name,
+            created_at=vector_store.created_at,
+            last_active_at=vector_store.last_active_at,
+            expires_at=vector_store.expires_at,
+            status=vector_store.status or "unknown",
+            usage_bytes=vector_store.usage_bytes or 0,
+            metadata=vector_store.metadata or None,
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store not found: %s", e)
+        response = NotFoundResponse(
+            resource="vector_store", resource_id=vector_store_id
+        )
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while updating vector store: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.delete(
+    "/vector-stores/{vector_store_id}",
+    responses={"204": {"description": "Vector store deleted"}},
+    status_code=status.HTTP_204_NO_CONTENT,
+)
+@authorize(Action.MANAGE_VECTOR_STORES)
+async def delete_vector_store(
+    request: Request,
+    vector_store_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+) -> None:
+    """Delete a vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store to delete.
+        auth: Authentication tuple from the auth dependency.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: Vector store not found
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        await client.vector_stores.delete(vector_store_id)
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store not found: %s", e)
+        response = NotFoundResponse(
+            resource="vector_store", resource_id=vector_store_id
+        )
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while deleting vector store: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.post("/files", responses=file_responses)
+@authorize(Action.MANAGE_FILES)
+async def create_file(  # pylint: disable=too-many-branches,too-many-statements
+    request: Request,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+    file: UploadFile = File(...),
+) -> FileResponse:
+    """Upload a file.
+
+    Parameters:
+        request: The incoming HTTP request.
+        auth: Authentication tuple from the auth dependency.
+        file: The file to upload.
+
+    Returns:
+        FileResponse: The uploaded file object.
+
+    Raises:
+        HTTPException:
+            - 400: Bad request (e.g., file too large, invalid format)
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+
+    check_configuration_loaded(configuration)
+
+    # Check Content-Length header BEFORE reading to prevent DoS via memory exhaustion
+    content_length = request.headers.get("content-length")
+    if content_length:
+        try:
+            size = int(content_length)
+            if size > DEFAULT_MAX_FILE_UPLOAD_SIZE:
+                response = FileTooLargeResponse(
+                    file_size=size,
+                    max_size=DEFAULT_MAX_FILE_UPLOAD_SIZE,
+                )
+                raise HTTPException(**response.model_dump())
+        except ValueError:
+            # Invalid Content-Length header, continue and validate after reading
+            pass
+
+    # file.size attribute if available
+    if hasattr(file, "size") and file.size is not None:
+        if file.size > DEFAULT_MAX_FILE_UPLOAD_SIZE:
+            response = FileTooLargeResponse(
+                file_size=file.size,
+                max_size=DEFAULT_MAX_FILE_UPLOAD_SIZE,
+            )
+            raise HTTPException(**response.model_dump())
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+
+        # Read file content once
+        content = await file.read()
+
+        # Verify actual size after reading
+        if len(content) > DEFAULT_MAX_FILE_UPLOAD_SIZE:
+            response = FileTooLargeResponse(
+                file_size=len(content),
+                max_size=DEFAULT_MAX_FILE_UPLOAD_SIZE,
+            )
+            raise HTTPException(**response.model_dump())
+
+        filename = file.filename or "uploaded_file"
+
+        # Add .txt extension if no extension present
+        # (since parsed PDFs/URLs are sent as plain text)
+        if not os.path.splitext(filename)[1]:
+            filename = f"{filename}.txt"
+
+        logger.info(
+            "Uploading file - filename: %s, size: %d bytes",
+            filename,
+            len(content),
+        )
+
+        file_bytes = BytesIO(content)
+        file_bytes.name = filename
+
+        file_obj = await client.files.create(
+            file=file_bytes,
+            purpose="assistants",
+        )
+
+        return FileResponse(
+            id=file_obj.id,
+            filename=file_obj.filename or filename,
+            bytes=file_obj.bytes or len(content),
+            created_at=file_obj.created_at,
+            purpose=file_obj.purpose or "assistants",
+            object=file_obj.object or "file",
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Bad request for file upload: %s", e)
+        # Check if backend rejected due to file size
+        error_msg = str(e).lower()
+        if "too large" in error_msg or "size" in error_msg or "exceeds" in error_msg:
+            response = FileTooLargeResponse(
+                response="Invalid file upload",
+                cause=f"File upload rejected by Llama Stack: {str(e)}",
+            )
+        else:
+            response = InternalServerErrorResponse.query_failed(
+                cause=f"File upload rejected by Llama Stack: {str(e)}"
+            )
+            # Override to use 400 status code since it's a client error
+            response.status_code = status.HTTP_400_BAD_REQUEST
+            response.detail.response = "Invalid file upload"
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while uploading file: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.post(
+    "/vector-stores/{vector_store_id}/files", responses=vector_store_file_responses
+)
+@authorize(Action.MANAGE_VECTOR_STORES)
+async def add_file_to_vector_store(  # pylint: disable=too-many-locals,too-many-statements
+    request: Request,
+    vector_store_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+    body: VectorStoreFileCreateRequest,
+) -> VectorStoreFileResponse:
+    """Add a file to a vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store.
+        auth: Authentication tuple from the auth dependency.
+        body: File addition parameters.
+
+    Returns:
+        VectorStoreFileResponse: The vector store file object.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: Vector store or file not found
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+
+        # Retry logic for database lock errors
+        max_retries = 3
+        retry_delay = 0.5  # seconds
+        vs_file = None
+        last_lock_error: Exception | None = None
+
+        for attempt in range(max_retries):
+            try:
+                vs_file = await client.vector_stores.files.create(
+                    vector_store_id=vector_store_id,
+                    **body.model_dump(exclude_none=True),
+                )
+                break  # Success, exit retry loop
+            except Exception as retry_error:  # pylint: disable=broad-exception-caught
+                error_msg = str(retry_error).lower()
+                is_lock_error = (
+                    "database is locked" in error_msg or "locked" in error_msg
+                )
+                is_last_attempt = attempt == max_retries - 1
+
+                if is_lock_error:
+                    last_lock_error = retry_error
+                    if not is_last_attempt:
+                        logger.warning(
+                            "Database locked while adding file to vector store, "
+                            "retrying in %s seconds (attempt %d/%d)",
+                            retry_delay,
+                            attempt + 1,
+                            max_retries,
+                        )
+                        await asyncio.sleep(retry_delay)
+                        retry_delay *= 2  # Exponential backoff
+                        continue
+                    break
+                raise  # Re-raise if not a lock error
+        if vs_file is None:
+            if last_lock_error is not None:
+                # Use standard error response model for consistency
+                response = InternalServerErrorResponse(
+                    response="Failed to create vector store file",
+                    cause="All retry attempts failed to create the vector store file",
+                )
+                raise HTTPException(**response.model_dump()) from last_lock_error
+            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
+        logger.info(
+            "Vector store file created - ID: %s, status: %s, last_error: %s",
+            vs_file.id,
+            vs_file.status,
+            vs_file.last_error if vs_file.last_error else "None",
+        )
+
+        return VectorStoreFileResponse(
+            id=vs_file.id,
+            vector_store_id=vs_file.vector_store_id or vector_store_id,
+            status=vs_file.status or "unknown",
+            attributes=vs_file.attributes,
+            last_error=(
+                vs_file.last_error.message
+                if vs_file.last_error and hasattr(vs_file.last_error, "message")
+                else None
+            ),
+            object=vs_file.object or "vector_store.file",
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store file operation failed: %s", e)
+        # Don't assume which resource is missing - could be vector_store_id OR file_id
+        response = NotFoundResponse(
+            resource="vector_store_or_file",
+            resource_id=f"vector_store={vector_store_id}, file={body.file_id}",
+        )
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while adding file to vector store: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.get(
+    "/vector-stores/{vector_store_id}/files",
+    responses=vector_store_files_list_responses,
+)
+@authorize(Action.READ_VECTOR_STORES)
+async def list_vector_store_files(
+    request: Request,
+    vector_store_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+) -> VectorStoreFilesListResponse:
+    """List files in a vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store.
+        auth: Authentication tuple from the auth dependency.
+
+    Returns:
+        VectorStoreFilesListResponse: List of files in the vector store.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: Vector store not found
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        files = await client.vector_stores.files.list(vector_store_id=vector_store_id)
+
+        data = [
+            VectorStoreFileResponse(
+                id=f.id,
+                vector_store_id=f.vector_store_id or vector_store_id,
+                status=f.status or "unknown",
+                attributes=f.attributes,
+                last_error=(
+                    f.last_error.message
+                    if f.last_error and hasattr(f.last_error, "message")
+                    else None
+                ),
+                object=f.object or "vector_store.file",
+            )
+            for f in files.data
+        ]
+        return VectorStoreFilesListResponse(data=data)
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store not found: %s", e)
+        response = NotFoundResponse(
+            resource="vector_store", resource_id=vector_store_id
+        )
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while listing vector store files: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.get(
+    "/vector-stores/{vector_store_id}/files/{file_id}",
+    responses=vector_store_file_responses,
+)
+@authorize(Action.READ_VECTOR_STORES)
+async def get_vector_store_file(
+    request: Request,
+    vector_store_id: str,
+    file_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+) -> VectorStoreFileResponse:
+    """Retrieve a file from a vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store.
+        file_id: ID of the file.
+        auth: Authentication tuple from the auth dependency.
+
+    Returns:
+        VectorStoreFileResponse: The vector store file object.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: File not found in vector store
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        vs_file = await client.vector_stores.files.retrieve(
+            vector_store_id=vector_store_id,
+            file_id=file_id,
+        )
+
+        return VectorStoreFileResponse(
+            id=vs_file.id,
+            vector_store_id=vs_file.vector_store_id or vector_store_id,
+            status=vs_file.status or "unknown",
+            attributes=vs_file.attributes,
+            last_error=(
+                vs_file.last_error.message
+                if vs_file.last_error and hasattr(vs_file.last_error, "message")
+                else None
+            ),
+            object=vs_file.object or "vector_store.file",
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store file not found: %s", e)
+        response = NotFoundResponse(resource="file", resource_id=file_id)
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while getting vector store file: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
+
+
+@router.delete(
+    "/vector-stores/{vector_store_id}/files/{file_id}",
+    responses={"204": {"description": "File deleted from vector store"}},
+    status_code=status.HTTP_204_NO_CONTENT,
+)
+@authorize(Action.MANAGE_VECTOR_STORES)
+async def delete_vector_store_file(
+    request: Request,
+    vector_store_id: str,
+    file_id: str,
+    auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
+) -> None:
+    """Delete a file from a vector store.
+
+    Parameters:
+        request: The incoming HTTP request.
+        vector_store_id: ID of the vector store.
+        file_id: ID of the file to delete.
+        auth: Authentication tuple from the auth dependency.
+
+    Raises:
+        HTTPException:
+            - 401: Authentication failed
+            - 403: Authorization failed
+            - 404: File not found in vector store
+            - 500: Lightspeed Stack configuration not loaded
+            - 503: Unable to connect to Llama Stack
+    """
+    _ = auth
+    _ = request
+
+    check_configuration_loaded(configuration)
+
+    try:
+        client = AsyncLlamaStackClientHolder().get_client()
+        await client.vector_stores.files.delete(
+            vector_store_id=vector_store_id,
+            file_id=file_id,
+        )
+    except APIConnectionError as e:
+        logger.error("Unable to connect to Llama Stack: %s", e)
+        response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e))
+        raise HTTPException(**response.model_dump()) from e
+    except BadRequestError as e:
+        logger.error("Vector store file not found: %s", e)
+        response = NotFoundResponse(resource="file", resource_id=file_id)
+        raise HTTPException(**response.model_dump()) from e
+    except (LLSApiStatusError, OpenAIAPIStatusError) as e:
+        logger.error("API status error while deleting vector store file: %s", e)
+        error_response = handle_known_apistatus_errors(e, "llama-stack")
+        raise HTTPException(**error_response.model_dump()) from e
diff --git a/src/app/routers.py b/src/app/routers.py
index 1841d1fc4..391864b4f 100644
--- a/src/app/routers.py
+++ b/src/app/routers.py
@@ -28,6 +28,7 @@
     stream_interrupt,
     streaming_query,
     tools,
+    vector_stores,
 )
 
 
@@ -53,6 +54,7 @@ def include_routers(app: FastAPI) -> None:
     app.include_router(shields.router, prefix="/v1")
     app.include_router(providers.router, prefix="/v1")
     app.include_router(rags.router, prefix="/v1")
+    app.include_router(vector_stores.router, prefix="/v1")
     # Query endpoints
     app.include_router(query.router, prefix="/v1")
     app.include_router(streaming_query.router, prefix="/v1")
diff --git a/src/constants.py b/src/constants.py
index 5f05431c6..76e06b90c 100644
--- a/src/constants.py
+++ b/src/constants.py
@@ -2,7 +2,7 @@
 
 # Minimal and maximal supported Llama Stack version
 MINIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.2.17"
-MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.5.2"
+MAXIMAL_SUPPORTED_LLAMA_STACK_VERSION = "0.5.4"
 
 UNABLE_TO_PROCESS_RESPONSE = "Unable to process this request"
 
@@ -128,6 +128,10 @@
 DEFAULT_AUTHENTICATION_MODULE = AUTH_MOD_NOOP
 # Maximum allowed size for base64-encoded x-rh-identity header (bytes)
 DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE = 8192
+
+# Maximum allowed file upload size (bytes) - 100MB default
+# Protects against DoS attacks via large file uploads
+DEFAULT_MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024  # 100 MB
 DEFAULT_JWT_UID_CLAIM = "user_id"
 DEFAULT_JWT_USER_NAME_CLAIM = "username"
 
diff --git a/src/models/config.py b/src/models/config.py
index 1b86e5437..3d69ecf2c 100644
--- a/src/models/config.py
+++ b/src/models/config.py
@@ -1030,6 +1030,11 @@ class Action(str, Enum):
     A2A_MESSAGE = "a2a_message"
     A2A_JSONRPC = "a2a_jsonrpc"
 
+    # Vector store management
+    MANAGE_VECTOR_STORES = "manage_vector_stores"
+    READ_VECTOR_STORES = "read_vector_stores"
+    MANAGE_FILES = "manage_files"
+
 
 class AccessRule(ConfigurationBase):
     """Rule defining what actions a role can perform."""
diff --git a/src/models/requests.py b/src/models/requests.py
index 7a8aba99c..635c1740a 100644
--- a/src/models/requests.py
+++ b/src/models/requests.py
@@ -1,5 +1,7 @@
 """Models for REST API requests."""
 
+# pylint: disable=too-many-lines
+
 from enum import Enum
 from typing import Any, Optional, Self
 
@@ -935,3 +937,213 @@ def validate_authorization_header_values(
                     "File-path based secrets are only supported in static YAML config."
                 )
         return value
+
+
+class VectorStoreCreateRequest(BaseModel):
+    """Model representing a request to create a vector store.
+
+    Attributes:
+        name: Name of the vector store.
+        embedding_model: Optional embedding model to use.
+        embedding_dimension: Optional embedding dimension.
+        chunking_strategy: Optional chunking strategy configuration.
+        provider_id: Optional vector store provider identifier.
+        metadata: Optional metadata dictionary for storing session information.
+    """
+
+    name: str = Field(
+        ...,
+        description="Name of the vector store",
+        examples=["my_vector_store"],
+        min_length=1,
+        max_length=256,
+    )
+
+    embedding_model: Optional[str] = Field(
+        None,
+        description="Embedding model to use for the vector store",
+        examples=["text-embedding-ada-002"],
+    )
+
+    embedding_dimension: Optional[int] = Field(
+        None,
+        description="Dimension of the embedding vectors",
+        examples=[1536],
+        gt=0,
+    )
+
+    chunking_strategy: Optional[dict[str, Any]] = Field(
+        None,
+        description="Chunking strategy configuration",
+        examples=[{"type": "fixed", "chunk_size": 512, "chunk_overlap": 50}],
+    )
+
+    provider_id: Optional[str] = Field(
+        None,
+        description="Vector store provider identifier",
+        examples=["rhdh-docs"],
+    )
+
+    metadata: Optional[dict[str, Any]] = Field(
+        None,
+        description="Metadata dictionary for storing session information",
+        examples=[{"user_id": "user123", "session_id": "sess456"}],
+    )
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "name": "my_vector_store",
+                    "embedding_model": "text-embedding-ada-002",
+                    "embedding_dimension": 1536,
+                    "provider_id": "rhdh-docs",
+                    "metadata": {"user_id": "user123"},
+                },
+            ]
+        },
+    }
+
+
+class VectorStoreUpdateRequest(BaseModel):
+    """Model representing a request to update a vector store.
+
+    Attributes:
+        name: New name for the vector store.
+        expires_at: Optional expiration timestamp.
+        metadata: Optional metadata dictionary for storing session information.
+    """
+
+    name: Optional[str] = Field(
+        None,
+        description="New name for the vector store",
+        examples=["updated_vector_store"],
+        min_length=1,
+        max_length=256,
+    )
+
+    expires_at: Optional[int] = Field(
+        None,
+        description="Unix timestamp when the vector store should expire",
+        examples=[1735689600],
+        gt=0,
+    )
+
+    metadata: Optional[dict[str, Any]] = Field(
+        None,
+        description="Metadata dictionary for storing session information",
+        examples=[{"user_id": "user123", "session_id": "sess456"}],
+    )
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "name": "updated_vector_store",
+                    "expires_at": 1735689600,
+                    "metadata": {"user_id": "user123"},
+                },
+            ]
+        },
+    }
+
+    @model_validator(mode="after")
+    def check_at_least_one_field(self) -> Self:
+        """Ensure at least one field is provided for update.
+
+        Raises:
+            ValueError: If all fields are None (empty update).
+
+        Returns:
+            Self: The validated model instance.
+        """
+        if self.name is None and self.expires_at is None and self.metadata is None:
+            raise ValueError(
+                "At least one field must be provided: name, expires_at, or metadata"
+            )
+        return self
+
+
+class VectorStoreFileCreateRequest(BaseModel):
+    """Model representing a request to add a file to a vector store.
+
+    Attributes:
+        file_id: ID of the file to add to the vector store.
+        attributes: Optional metadata key-value pairs (max 16 pairs).
+        chunking_strategy: Optional chunking strategy configuration.
+    """
+
+    file_id: str = Field(
+        ...,
+        description="ID of the file to add to the vector store",
+        examples=["file-abc123"],
+        min_length=1,
+    )
+
+    attributes: Optional[dict[str, str | float | bool]] = Field(
+        None,
+        description=(
+            "Set of up to 16 key-value pairs for storing additional information. "
+            "Keys: strings (max 64 chars). Values: strings (max 512 chars), booleans, or numbers."
+        ),
+        examples=[
+            {"created_at": "2026-04-04T15:20:00Z", "updated_at": "2026-04-04T15:20:00Z"}
+        ],
+    )
+
+    chunking_strategy: Optional[dict[str, Any]] = Field(
+        None,
+        description="Chunking strategy configuration for this file",
+        examples=[{"type": "fixed", "chunk_size": 512, "chunk_overlap": 50}],
+    )
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "file_id": "file-abc123",
+                    "attributes": {"created_at": "2026-04-04T15:20:00Z"},
+                    "chunking_strategy": {"type": "fixed", "chunk_size": 512},
+                },
+            ]
+        },
+    }
+
+    @field_validator("attributes")
+    @classmethod
+    def validate_attributes(
+        cls, value: Optional[dict[str, str | float | bool]]
+    ) -> Optional[dict[str, str | float | bool]]:
+        """Validate attributes field constraints.
+
+        Ensures:
+        - Maximum 16 key-value pairs
+        - Keys are max 64 characters
+        - String values are max 512 characters
+
+        Parameters:
+            value: The attributes dictionary to validate.
+
+        Raises:
+            ValueError: If constraints are violated.
+
+        Returns:
+            The validated attributes dictionary.
+        """
+        if value is None:
+            return value
+
+        if len(value) > 16:
+            raise ValueError(f"attributes can have at most 16 pairs, got {len(value)}")
+
+        for key, val in value.items():
+            if len(key) > 64:
+                raise ValueError(f"attribute key '{key}' exceeds 64 characters")
+
+            if isinstance(val, str) and len(val) > 512:
+                raise ValueError(f"attribute value for '{key}' exceeds 512 characters")
+
+        return value
diff --git a/src/models/responses.py b/src/models/responses.py
index 5c2e974cc..71e55934e 100644
--- a/src/models/responses.py
+++ b/src/models/responses.py
@@ -1804,7 +1804,14 @@ class BadRequestResponse(AbstractErrorResponse):
                             "123e4567-e89b-12d3-a456-426614174000 has invalid format."
                         ),
                     },
-                }
+                },
+                {
+                    "label": "file_upload",
+                    "detail": {
+                        "response": "Invalid file upload",
+                        "cause": "File upload rejected: Invalid file format",
+                    },
+                },
             ]
         }
     }
@@ -2115,6 +2122,20 @@ class NotFoundResponse(AbstractErrorResponse):
                         "cause": "Mcp Server with ID test-mcp-server does not exist",
                     },
                 },
+                {
+                    "label": "vector store",
+                    "detail": {
+                        "response": "Vector Store not found",
+                        "cause": "Vector Store with ID vs_abc123 does not exist",
+                    },
+                },
+                {
+                    "label": "file",
+                    "detail": {
+                        "response": "File not found",
+                        "cause": "File with ID file_abc123 does not exist",
+                    },
+                },
             ]
         }
     }
@@ -2221,6 +2242,66 @@ def __init__(
         )
 
 
+class FileTooLargeResponse(AbstractErrorResponse):
+    """413 Content Too Large - File upload exceeds size limit."""
+
+    description: ClassVar[str] = "File upload exceeds size limit"
+    model_config = {
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "label": "file upload",
+                    "detail": {
+                        "response": "File too large",
+                        "cause": (
+                            "File size 150000000 bytes exceeds maximum "
+                            "allowed size of 104857600 bytes (100 MB)"
+                        ),
+                    },
+                },
+                {
+                    "label": "backend rejection",
+                    "detail": {
+                        "response": "Invalid file upload",
+                        "cause": "File upload rejected by Llama Stack: File size exceeds limit",
+                    },
+                },
+            ]
+        }
+    }
+
+    def __init__(
+        self,
+        *,
+        response: str = "File too large",
+        cause: str | None = None,
+        file_size: int | None = None,
+        max_size: int | None = None,
+    ) -> None:
+        """Initialize a FileTooLargeResponse.
+
+        Args:
+            response: Short summary of the error. Defaults to "File too large".
+            cause: Detailed explanation. If not provided, will be generated from
+                file_size and max_size.
+            file_size: The size of the uploaded file in bytes.
+            max_size: The maximum allowed file size in bytes.
+        """
+        if cause is None and file_size is not None and max_size is not None:
+            cause = (
+                f"File size {file_size} bytes exceeds maximum allowed "
+                f"size of {max_size} bytes ({max_size // (1024 * 1024)} MB)"
+            )
+        elif cause is None:
+            cause = "The uploaded file exceeds the maximum allowed size."
+
+        super().__init__(
+            response=response,
+            cause=cause,
+            status_code=status.HTTP_413_CONTENT_TOO_LARGE,
+        )
+
+
 class UnprocessableEntityResponse(AbstractErrorResponse):
     """422 Unprocessable Entity - Request validation failed."""
 
@@ -2604,3 +2685,228 @@ def __init__(self, *, backend_name: str, cause: str):
             cause=cause,
             status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
         )
+
+
+class VectorStoreResponse(AbstractSuccessfulResponse):
+    """Response model containing a single vector store.
+
+    Attributes:
+        id: Vector store ID.
+        name: Vector store name.
+        created_at: Unix timestamp when created.
+        last_active_at: Unix timestamp of last activity.
+        expires_at: Optional Unix timestamp when it expires.
+        status: Vector store status.
+        usage_bytes: Storage usage in bytes.
+        metadata: Optional metadata dictionary for storing session information.
+    """
+
+    id: str = Field(..., description="Vector store ID")
+    name: str = Field(..., description="Vector store name")
+    created_at: int = Field(..., description="Unix timestamp when created")
+    last_active_at: Optional[int] = Field(
+        None, description="Unix timestamp of last activity"
+    )
+    expires_at: Optional[int] = Field(
+        None, description="Unix timestamp when it expires"
+    )
+    status: str = Field(..., description="Vector store status")
+    usage_bytes: int = Field(default=0, description="Storage usage in bytes")
+    metadata: Optional[dict[str, Any]] = Field(
+        None,
+        description="Metadata dictionary for storing session information",
+        examples=[
+            {"conversation_id": "conv_123", "document_ids": ["doc_456", "doc_789"]}
+        ],
+    )
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "id": "vs_abc123",
+                    "name": "customer_support_docs",
+                    "created_at": 1704067200,
+                    "last_active_at": 1704153600,
+                    "expires_at": None,
+                    "status": "active",
+                    "usage_bytes": 1048576,
+                    "metadata": {
+                        "conversation_id": "conv_123",
+                        "document_ids": ["doc_456", "doc_789"],
+                    },
+                }
+            ]
+        },
+    }
+
+
+class VectorStoresListResponse(AbstractSuccessfulResponse):
+    """Response model containing a list of vector stores.
+
+    Attributes:
+        data: List of vector store objects.
+        object: Object type (always "list").
+    """
+
+    data: list[VectorStoreResponse] = Field(
+        default_factory=list, description="List of vector stores"
+    )
+    object: str = Field(default="list", description="Object type")
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "data": [
+                        {
+                            "id": "vs_abc123",
+                            "name": "customer_support_docs",
+                            "created_at": 1704067200,
+                            "last_active_at": 1704153600,
+                            "expires_at": None,
+                            "status": "active",
+                            "usage_bytes": 1048576,
+                            "metadata": {"conversation_id": "conv_123"},
+                        },
+                        {
+                            "id": "vs_def456",
+                            "name": "product_documentation",
+                            "created_at": 1704070800,
+                            "last_active_at": 1704157200,
+                            "expires_at": None,
+                            "status": "active",
+                            "usage_bytes": 2097152,
+                            "metadata": None,
+                        },
+                    ],
+                    "object": "list",
+                }
+            ]
+        },
+    }
+
+
+class FileResponse(AbstractSuccessfulResponse):
+    """Response model containing a file object.
+
+    Attributes:
+        id: File ID.
+        filename: File name.
+        bytes: File size in bytes.
+        created_at: Unix timestamp when created.
+        purpose: File purpose.
+        object: Object type (always "file").
+    """
+
+    id: str = Field(..., description="File ID")
+    filename: str = Field(..., description="File name")
+    bytes: int = Field(..., description="File size in bytes")
+    created_at: int = Field(..., description="Unix timestamp when created")
+    purpose: str = Field(default="assistants", description="File purpose")
+    object: str = Field(default="file", description="Object type")
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "id": "file_abc123",
+                    "filename": "documentation.pdf",
+                    "bytes": 524288,
+                    "created_at": 1704067200,
+                    "purpose": "assistants",
+                    "object": "file",
+                }
+            ]
+        },
+    }
+
+
+class VectorStoreFileResponse(AbstractSuccessfulResponse):
+    """Response model containing a vector store file object.
+
+    Attributes:
+        id: Vector store file ID.
+        vector_store_id: ID of the vector store.
+        status: File processing status.
+        attributes: Optional metadata key-value pairs.
+        last_error: Optional error message if processing failed.
+        object: Object type (always "vector_store.file").
+    """
+
+    id: str = Field(..., description="Vector store file ID")
+    vector_store_id: str = Field(..., description="ID of the vector store")
+    status: str = Field(..., description="File processing status")
+    attributes: Optional[dict[str, str | float | bool]] = Field(
+        None,
+        description=(
+            "Set of up to 16 key-value pairs for storing additional information. "
+            "Keys: strings (max 64 chars). Values: strings (max 512 chars), booleans, or numbers."
+        ),
+    )
+    last_error: Optional[str] = Field(
+        None, description="Error message if processing failed"
+    )
+    object: str = Field(default="vector_store.file", description="Object type")
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "id": "file_abc123",
+                    "vector_store_id": "vs_abc123",
+                    "status": "completed",
+                    "attributes": {"chunk_size": "512", "indexed": True},
+                    "last_error": None,
+                    "object": "vector_store.file",
+                }
+            ]
+        },
+    }
+
+
+class VectorStoreFilesListResponse(AbstractSuccessfulResponse):
+    """Response model containing a list of vector store files.
+
+    Attributes:
+        data: List of vector store file objects.
+        object: Object type (always "list").
+    """
+
+    data: list[VectorStoreFileResponse] = Field(
+        default_factory=list, description="List of vector store files"
+    )
+    object: str = Field(default="list", description="Object type")
+
+    model_config = {
+        "extra": "forbid",
+        "json_schema_extra": {
+            "examples": [
+                {
+                    "data": [
+                        {
+                            "id": "file_abc123",
+                            "vector_store_id": "vs_abc123",
+                            "status": "completed",
+                            "attributes": {"chunk_size": "512"},
+                            "last_error": None,
+                            "object": "vector_store.file",
+                        },
+                        {
+                            "id": "file_def456",
+                            "vector_store_id": "vs_abc123",
+                            "status": "processing",
+                            "attributes": None,
+                            "last_error": None,
+                            "object": "vector_store.file",
+                        },
+                    ],
+                    "object": "list",
+                }
+            ]
+        },
+    }
diff --git a/src/observability/README.md b/src/observability/README.md
index 0232571da..d574eab03 100644
--- a/src/observability/README.md
+++ b/src/observability/README.md
@@ -30,7 +30,7 @@ event_data = InferenceEventData(
     org_id="12345678",
     system_id="abc-def-123",
     request_id="req_xyz789",
-    cla_version="CLA/0.5.0",
+    cla_version="CLA/0.5.1",
     system_os="RHEL",
     system_version="9.3",
     system_arch="x86_64",
diff --git a/src/version.py b/src/version.py
index e863b7c7c..a96388aeb 100644
--- a/src/version.py
+++ b/src/version.py
@@ -9,4 +9,4 @@
 # [tool.pdm.version]
 # source = "file"
 # path = "src/version.py"
-__version__ = "0.5.0"
+__version__ = "0.5.1"
diff --git a/tests/e2e/features/info.feature b/tests/e2e/features/info.feature
index 8c1431790..3d53fa2d4 100644
--- a/tests/e2e/features/info.feature
+++ b/tests/e2e/features/info.feature
@@ -15,7 +15,7 @@ Feature: Info tests
     Given The system is in default state
      When I access REST API endpoint "info" using HTTP GET method
      Then The status code of the response is 200
-      And The body of the response has proper name Lightspeed Core Service (LCS) and version 0.5.0
+      And The body of the response has proper name Lightspeed Core Service (LCS) and version 0.5.1
       And The body of the response has llama-stack version 0.5.2
 
   @skip-in-library-mode
diff --git a/tests/integration/endpoints/test_rlsapi_v1_integration.py b/tests/integration/endpoints/test_rlsapi_v1_integration.py
index ae59acaab..4a8ec32a3 100644
--- a/tests/integration/endpoints/test_rlsapi_v1_integration.py
+++ b/tests/integration/endpoints/test_rlsapi_v1_integration.py
@@ -44,7 +44,7 @@ def _create_mock_request(mocker: MockerFixture) -> Any:
     mock_request = mocker.Mock()
     # Use spec=[] to create a Mock with no attributes, simulating absent rh_identity_data
     mock_request.state = mocker.Mock(spec=[])
-    mock_request.headers = {"User-Agent": "CLA/0.5.0"}
+    mock_request.headers = {"User-Agent": "CLA/0.5.1"}
     return mock_request
 
 
diff --git a/tests/unit/app/endpoints/conftest.py b/tests/unit/app/endpoints/conftest.py
index 56e3b821e..5def43273 100644
--- a/tests/unit/app/endpoints/conftest.py
+++ b/tests/unit/app/endpoints/conftest.py
@@ -17,7 +17,7 @@ def mock_request_factory_fixture(mocker: MockerFixture) -> Callable[..., Any]:
 
     def _create(rh_identity: Any = None) -> Any:
         mock_request = mocker.Mock()
-        mock_request.headers = {"User-Agent": "CLA/0.5.0"}
+        mock_request.headers = {"User-Agent": "CLA/0.5.1"}
 
         if rh_identity is not None:
             mock_request.state = mocker.Mock()
diff --git a/tests/unit/app/endpoints/test_vector_stores.py b/tests/unit/app/endpoints/test_vector_stores.py
new file mode 100644
index 000000000..f257f68d3
--- /dev/null
+++ b/tests/unit/app/endpoints/test_vector_stores.py
@@ -0,0 +1,1179 @@
+"""Unit tests for the /vector-stores REST API endpoints."""
+
+# pylint: disable=too-many-lines
+
+from typing import Any
+
+import pytest
+from fastapi import HTTPException, Request, status
+from llama_stack_client import APIConnectionError, BadRequestError
+from pytest_mock import MockerFixture
+
+from app.endpoints.vector_stores import (
+    add_file_to_vector_store,
+    create_file,
+    create_vector_store,
+    delete_vector_store,
+    delete_vector_store_file,
+    get_vector_store,
+    get_vector_store_file,
+    list_vector_store_files,
+    list_vector_stores,
+    update_vector_store,
+)
+from authentication.interface import AuthTuple
+from configuration import AppConfig
+from models.requests import (
+    VectorStoreCreateRequest,
+    VectorStoreFileCreateRequest,
+    VectorStoreUpdateRequest,
+)
+from tests.unit.utils.auth_helpers import mock_authorization_resolvers
+
+
+# pylint: disable=R0903,R0902
+class VectorStore:
+    """Mock vector store object."""
+
+    def __init__(
+        self,
+        vs_id: str,
+        name: str,
+        created_at: int = 1735689600,
+        vs_status: str = "active",
+    ) -> None:
+        """Initialize vector store mock."""
+        self.id = vs_id
+        self.name = name
+        self.created_at = created_at
+        self.last_active_at = created_at
+        self.expires_at = None
+        self.object = "vector_store"
+        self.status = vs_status
+        self.usage_bytes = 0
+        self.metadata = None
+
+
+# pylint: disable=R0903
+class VectorStoresList:
+    """Mock vector stores list."""
+
+    def __init__(self, stores: list[VectorStore]) -> None:
+        """Initialize vector stores list mock."""
+        self.data = stores
+
+
+# pylint: disable=R0903
+class File:
+    """Mock file object."""
+
+    def __init__(self, file_id: str, filename: str, file_bytes: int = 1024) -> None:
+        """Initialize file mock."""
+        self.id = file_id
+        self.filename = filename
+        self.bytes = file_bytes
+        self.created_at = 1735689600
+        self.purpose = "assistants"
+        self.object = "file"
+
+
+# pylint: disable=R0903
+class VectorStoreFile:
+    """Mock vector store file object."""
+
+    def __init__(
+        self, file_id: str, vector_store_id: str, file_status: str = "completed"
+    ) -> None:
+        """Initialize vector store file mock."""
+        self.id = file_id
+        self.vector_store_id = vector_store_id
+        self.created_at = 1735689600
+        self.status = file_status
+        self.attributes = None
+        self.last_error = None
+        self.object = "vector_store.file"
+
+
+# pylint: disable=R0903
+class VectorStoreFilesList:
+    """Mock vector store files list."""
+
+    def __init__(self, files: list[VectorStoreFile]) -> None:
+        """Initialize vector store files list mock."""
+        self.data = files
+
+
+def get_test_config() -> dict[str, Any]:
+    """Get test configuration dictionary.
+
+    Returns:
+        Test configuration dictionary.
+    """
+    return {
+        "name": "foo",
+        "service": {
+            "host": "localhost",
+            "port": 8080,
+            "auth_enabled": False,
+            "workers": 1,
+            "color_log": True,
+            "access_log": True,
+        },
+        "llama_stack": {
+            "api_key": "xyzzy",
+            "url": "http://x.y.com:1234",
+            "use_as_library_client": False,
+        },
+        "user_data_collection": {
+            "feedback_enabled": False,
+        },
+        "customization": None,
+        "authorization": {"access_rules": []},
+        "authentication": {"module": "noop"},
+    }
+
+
+def get_test_request() -> Request:
+    """Get test request object.
+
+    Returns:
+        Test request object.
+    """
+    return Request(
+        scope={
+            "type": "http",
+            "headers": [(b"authorization", b"Bearer test-token")],
+        }
+    )
+
+
+def get_test_auth() -> AuthTuple:
+    """Get test auth tuple.
+
+    Returns:
+        Test auth tuple.
+    """
+    return ("test_user_id", "test_user", True, "test_token")
+
+
+@pytest.mark.asyncio
+async def test_create_vector_store_configuration_not_loaded(
+    mocker: MockerFixture,
+) -> None:
+    """Test create vector store endpoint if configuration is not loaded."""
+    mock_authorization_resolvers(mocker)
+
+    mock_config = AppConfig()
+    mocker.patch("app.endpoints.vector_stores.configuration", mock_config)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreCreateRequest(name="test_store")
+
+    with pytest.raises(HTTPException) as e:
+        await create_vector_store(request=request, auth=auth, body=body)
+
+    assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
+    assert e.value.detail["response"] == "Configuration is not loaded"  # type: ignore
+
+
+@pytest.mark.asyncio
+async def test_create_vector_store_success(mocker: MockerFixture) -> None:
+    """Test successful vector store creation."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.create.return_value = VectorStore("vs_123", "test_store")
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreCreateRequest(name="test_store")
+
+    response = await create_vector_store(request=request, auth=auth, body=body)
+    assert response is not None
+    assert response.id == "vs_123"
+    assert response.name == "test_store"
+    assert response.status == "active"
+
+
+@pytest.mark.asyncio
+async def test_create_vector_store_connection_error(mocker: MockerFixture) -> None:
+    """Test create vector store with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.create.side_effect = APIConnectionError(request=None)  # type: ignore
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreCreateRequest(name="test_store")
+
+    with pytest.raises(HTTPException) as e:
+        await create_vector_store(request=request, auth=auth, body=body)
+
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+    assert e.value.detail["response"] == "Unable to connect to Llama Stack"  # type: ignore
+
+
+@pytest.mark.asyncio
+async def test_list_vector_stores_success(mocker: MockerFixture) -> None:
+    """Test successful vector stores list."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.list.return_value = VectorStoresList(
+        [VectorStore("vs_1", "store1"), VectorStore("vs_2", "store2")]
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    response = await list_vector_stores(request=request, auth=auth)
+    assert response is not None
+    assert len(response.data) == 2
+    assert response.data[0].id == "vs_1"
+    assert response.data[1].id == "vs_2"
+
+
+@pytest.mark.asyncio
+async def test_get_vector_store_success(mocker: MockerFixture) -> None:
+    """Test successful vector store retrieval."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.retrieve.return_value = VectorStore(
+        "vs_123", "test_store"
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    response = await get_vector_store(
+        request=request, vector_store_id="vs_123", auth=auth
+    )
+    assert response is not None
+    assert response.id == "vs_123"
+    assert response.name == "test_store"
+
+
+@pytest.mark.asyncio
+async def test_get_vector_store_not_found(mocker: MockerFixture) -> None:
+    """Test vector store retrieval with not found error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    # Create a mock response for BadRequestError
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.retrieve.side_effect = BadRequestError(
+        message="Not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await get_vector_store(request=request, vector_store_id="vs_999", auth=auth)
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
+
+
+@pytest.mark.asyncio
+async def test_update_vector_store_success(mocker: MockerFixture) -> None:
+    """Test successful vector store update."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.update.return_value = VectorStore(
+        "vs_123", "updated_store"
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreUpdateRequest(name="updated_store")
+
+    response = await update_vector_store(
+        request=request, vector_store_id="vs_123", auth=auth, body=body
+    )
+    assert response is not None
+    assert response.id == "vs_123"
+    assert response.name == "updated_store"
+
+
+@pytest.mark.asyncio
+async def test_delete_vector_store_success(mocker: MockerFixture) -> None:
+    """Test successful vector store deletion."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.delete.return_value = None
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    response = await delete_vector_store(
+        request=request, vector_store_id="vs_123", auth=auth
+    )
+    assert response is None
+
+
+@pytest.mark.asyncio
+async def test_create_file_success(mocker: MockerFixture) -> None:
+    """Test successful file upload."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.files.create.return_value = File("file_123", "test.txt", 1024)
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    # Mock UploadFile
+    mock_file = mocker.AsyncMock()
+    mock_file.filename = "test.txt"
+    mock_file.size = 12  # Size of "test content"
+    mock_file.read.return_value = b"test content"
+
+    response = await create_file(request=request, auth=auth, file=mock_file)
+    assert response is not None
+    assert response.id == "file_123"
+    assert response.filename == "test.txt"
+    assert response.bytes == 1024
+
+
+@pytest.mark.asyncio
+async def test_add_file_to_vector_store_success(mocker: MockerFixture) -> None:
+    """Test successfully adding file to vector store."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.create.return_value = VectorStoreFile(
+        "file_123", "vs_123"
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreFileCreateRequest(file_id="file_123")
+
+    response = await add_file_to_vector_store(
+        request=request, vector_store_id="vs_123", auth=auth, body=body
+    )
+    assert response is not None
+    assert response.id == "file_123"
+    assert response.vector_store_id == "vs_123"
+    assert response.status == "completed"
+
+
+@pytest.mark.asyncio
+async def test_add_file_to_vector_store_retry_on_database_lock(
+    mocker: MockerFixture,
+) -> None:
+    """Test retry logic when database lock error occurs."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    # First call raises database lock error, second call succeeds
+    mock_client.vector_stores.files.create.side_effect = [
+        Exception("database is locked"),
+        VectorStoreFile("file_123", "vs_123"),
+    ]
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    # Mock asyncio.sleep to avoid actual delays in tests
+    mock_sleep = mocker.patch("app.endpoints.vector_stores.asyncio.sleep")
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreFileCreateRequest(file_id="file_123")
+
+    response = await add_file_to_vector_store(
+        request=request, vector_store_id="vs_123", auth=auth, body=body
+    )
+    assert response is not None
+    assert response.id == "file_123"
+    assert response.vector_store_id == "vs_123"
+    assert response.status == "completed"
+
+    # Verify retry logic was triggered
+    assert mock_client.vector_stores.files.create.call_count == 2
+    # Verify sleep was called once with 0.5 seconds (first retry delay)
+    mock_sleep.assert_called_once_with(0.5)
+
+
+@pytest.mark.asyncio
+async def test_add_file_to_vector_store_max_retries_exceeded(
+    mocker: MockerFixture,
+) -> None:
+    """Test that max retries are respected when database lock persists."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    # All attempts fail with database lock error
+    mock_client.vector_stores.files.create.side_effect = Exception("database is locked")
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    # Mock asyncio.sleep to avoid actual delays in tests
+    mock_sleep = mocker.patch("app.endpoints.vector_stores.asyncio.sleep")
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreFileCreateRequest(file_id="file_123")
+
+    with pytest.raises(HTTPException) as e:
+        await add_file_to_vector_store(
+            request=request, vector_store_id="vs_123", auth=auth, body=body
+        )
+    assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
+
+    # Verify all 3 retry attempts were made
+    assert mock_client.vector_stores.files.create.call_count == 3
+    # Verify exponential backoff: 0.5s, then 1s (0.5 * 2)
+    assert mock_sleep.call_count == 2
+    assert mock_sleep.call_args_list[0][0][0] == 0.5
+    assert mock_sleep.call_args_list[1][0][0] == 1.0
+
+
+@pytest.mark.asyncio
+async def test_add_file_to_vector_store_non_lock_error_no_retry(
+    mocker: MockerFixture,
+) -> None:
+    """Test that non-lock errors are not retried.
+
+    Non-lock errors are re-raised and handled by global exception middleware.
+    """
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    # Raise a non-lock error
+    mock_client.vector_stores.files.create.side_effect = Exception("Some other error")
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    # Mock asyncio.sleep to verify it's not called
+    mock_sleep = mocker.patch("app.endpoints.vector_stores.asyncio.sleep")
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreFileCreateRequest(file_id="file_123")
+
+    # Non-lock errors are re-raised as-is (global middleware handles them)
+    with pytest.raises(Exception, match="Some other error"):
+        await add_file_to_vector_store(
+            request=request, vector_store_id="vs_123", auth=auth, body=body
+        )
+
+    # Verify only one attempt was made (no retries for non-lock errors)
+    assert mock_client.vector_stores.files.create.call_count == 1
+    # Verify sleep was not called (no retry)
+    mock_sleep.assert_not_called()
+
+
+@pytest.mark.asyncio
+async def test_list_vector_store_files_success(mocker: MockerFixture) -> None:
+    """Test successfully listing files in vector store."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.list.return_value = VectorStoreFilesList(
+        [
+            VectorStoreFile("file_1", "vs_123"),
+            VectorStoreFile("file_2", "vs_123"),
+        ]
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    response = await list_vector_store_files(
+        request=request, vector_store_id="vs_123", auth=auth
+    )
+    assert response is not None
+    assert len(response.data) == 2
+    assert response.data[0].id == "file_1"
+    assert response.data[1].id == "file_2"
+
+
+@pytest.mark.asyncio
+async def test_get_vector_store_file_success(mocker: MockerFixture) -> None:
+    """Test successfully retrieving file from vector store."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.retrieve.return_value = VectorStoreFile(
+        "file_123", "vs_123"
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    response = await get_vector_store_file(
+        request=request, vector_store_id="vs_123", file_id="file_123", auth=auth
+    )
+    assert response is not None
+    assert response.id == "file_123"
+    assert response.vector_store_id == "vs_123"
+
+
+@pytest.mark.asyncio
+async def test_delete_vector_store_file_success(mocker: MockerFixture) -> None:
+    """Test successfully deleting file from vector store."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.delete.return_value = None
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    response = await delete_vector_store_file(
+        request=request, vector_store_id="vs_123", file_id="file_123", auth=auth
+    )
+    assert response is None
+
+
+# Additional error path tests
+
+
+@pytest.mark.asyncio
+async def test_list_vector_stores_connection_error(mocker: MockerFixture) -> None:
+    """Test list vector stores with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.list.side_effect = APIConnectionError(request=None)  # type: ignore
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await list_vector_stores(request=request, auth=auth)
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_update_vector_store_connection_error(mocker: MockerFixture) -> None:
+    """Test update vector store with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.update.side_effect = APIConnectionError(request=None)  # type: ignore
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreUpdateRequest(name="updated_store")
+
+    with pytest.raises(HTTPException) as e:
+        await update_vector_store(
+            request=request, vector_store_id="vs_123", auth=auth, body=body
+        )
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_update_vector_store_not_found(mocker: MockerFixture) -> None:
+    """Test update vector store with not found error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.update.side_effect = BadRequestError(
+        message="Not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreUpdateRequest(name="updated_store")
+
+    with pytest.raises(HTTPException) as e:
+        await update_vector_store(
+            request=request, vector_store_id="vs_999", auth=auth, body=body
+        )
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
+
+
+@pytest.mark.asyncio
+async def test_delete_vector_store_connection_error(mocker: MockerFixture) -> None:
+    """Test delete vector store with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.delete.side_effect = APIConnectionError(request=None)  # type: ignore
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await delete_vector_store(request=request, vector_store_id="vs_123", auth=auth)
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_delete_vector_store_not_found(mocker: MockerFixture) -> None:
+    """Test delete vector store with not found error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.delete.side_effect = BadRequestError(
+        message="Not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await delete_vector_store(request=request, vector_store_id="vs_999", auth=auth)
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
+
+
+@pytest.mark.asyncio
+async def test_create_file_connection_error(mocker: MockerFixture) -> None:
+    """Test create file with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.files.create.side_effect = APIConnectionError(request=None)  # type: ignore
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    mock_file = mocker.AsyncMock()
+    mock_file.filename = "test.txt"
+    mock_file.size = 12  # Size of "test content"
+    mock_file.read.return_value = b"test content"
+
+    with pytest.raises(HTTPException) as e:
+        await create_file(request=request, auth=auth, file=mock_file)
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_create_file_bad_request(mocker: MockerFixture) -> None:
+    """Test create file with bad request error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.files.create.side_effect = BadRequestError(
+        message="File too large", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    mock_file = mocker.AsyncMock()
+    mock_file.filename = "test.txt"
+    mock_file.size = 12  # Size of "test content"
+    mock_file.read.return_value = b"test content"
+
+    with pytest.raises(HTTPException) as e:
+        await create_file(request=request, auth=auth, file=mock_file)
+
+    assert e.value.status_code == status.HTTP_413_CONTENT_TOO_LARGE
+
+
+@pytest.mark.asyncio
+async def test_create_file_too_large(mocker: MockerFixture) -> None:
+    """Test create file with file size exceeding limit."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    # Create a mock file that exceeds the size limit
+    mock_file = mocker.AsyncMock()
+    mock_file.filename = "large_file.pdf"
+    mock_file.size = 200 * 1024 * 1024  # 200 MB (exceeds 100 MB limit)
+    mock_file.read.side_effect = AssertionError("File too large")
+
+    with pytest.raises(HTTPException) as e:
+        await create_file(request=request, auth=auth, file=mock_file)
+
+    assert e.value.status_code == status.HTTP_413_CONTENT_TOO_LARGE
+    assert "too large" in str(e.value.detail).lower()
+
+
+@pytest.mark.asyncio
+async def test_create_file_content_length_too_large(mocker: MockerFixture) -> None:
+    """Test create file with Content-Length header exceeding limit."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    # Create request with large Content-Length header
+    request = Request(
+        scope={
+            "type": "http",
+            "headers": [
+                (b"authorization", b"Bearer test-token"),
+                (b"content-length", b"209715200"),  # 200 MB
+            ],
+        }
+    )
+    auth = get_test_auth()
+
+    # Create a mock file
+    mock_file = mocker.AsyncMock()
+    mock_file.filename = "large_file.pdf"
+    mock_file.size = None  # No size attribute
+
+    with pytest.raises(HTTPException) as e:
+        await create_file(request=request, auth=auth, file=mock_file)
+
+    assert e.value.status_code == status.HTTP_413_CONTENT_TOO_LARGE
+    assert "too large" in str(e.value.detail).lower()
+
+
+@pytest.mark.asyncio
+async def test_add_file_to_vector_store_connection_error(
+    mocker: MockerFixture,
+) -> None:
+    """Test add file to vector store with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.create.side_effect = APIConnectionError(
+        request=None  # type: ignore
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreFileCreateRequest(file_id="file_123")
+
+    with pytest.raises(HTTPException) as e:
+        await add_file_to_vector_store(
+            request=request, vector_store_id="vs_123", auth=auth, body=body
+        )
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_add_file_to_vector_store_not_found(mocker: MockerFixture) -> None:
+    """Test add file to vector store with not found error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.files.create.side_effect = BadRequestError(
+        message="File not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+    body = VectorStoreFileCreateRequest(file_id="file_999")
+
+    with pytest.raises(HTTPException) as e:
+        await add_file_to_vector_store(
+            request=request, vector_store_id="vs_123", auth=auth, body=body
+        )
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
+
+
+@pytest.mark.asyncio
+async def test_list_vector_store_files_connection_error(
+    mocker: MockerFixture,
+) -> None:
+    """Test list vector store files with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.list.side_effect = APIConnectionError(
+        request=None  # type: ignore
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await list_vector_store_files(
+            request=request, vector_store_id="vs_123", auth=auth
+        )
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_list_vector_store_files_not_found(mocker: MockerFixture) -> None:
+    """Test list vector store files with invalid vector store ID."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.files.list.side_effect = BadRequestError(
+        message="Vector store not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await list_vector_store_files(
+            request=request, vector_store_id="vs_999", auth=auth
+        )
+
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
+
+
+@pytest.mark.asyncio
+async def test_get_vector_store_file_connection_error(mocker: MockerFixture) -> None:
+    """Test get vector store file with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.retrieve.side_effect = APIConnectionError(
+        request=None  # type: ignore
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await get_vector_store_file(
+            request=request, vector_store_id="vs_123", file_id="file_123", auth=auth
+        )
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_get_vector_store_file_not_found(mocker: MockerFixture) -> None:
+    """Test get vector store file with not found error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.files.retrieve.side_effect = BadRequestError(
+        message="File not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await get_vector_store_file(
+            request=request, vector_store_id="vs_123", file_id="file_999", auth=auth
+        )
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
+
+
+@pytest.mark.asyncio
+async def test_delete_vector_store_file_connection_error(
+    mocker: MockerFixture,
+) -> None:
+    """Test delete vector store file with connection error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_client.vector_stores.files.delete.side_effect = APIConnectionError(
+        request=None  # type: ignore
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await delete_vector_store_file(
+            request=request, vector_store_id="vs_123", file_id="file_123", auth=auth
+        )
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+
+
+@pytest.mark.asyncio
+async def test_delete_vector_store_file_not_found(mocker: MockerFixture) -> None:
+    """Test delete vector store file with not found error."""
+    mock_authorization_resolvers(mocker)
+
+    config_dict = get_test_config()
+    cfg = AppConfig()
+    cfg.init_from_dict(config_dict)
+
+    mock_client = mocker.AsyncMock()
+    mock_response = mocker.Mock()
+    mock_response.request = mocker.Mock()
+    mock_client.vector_stores.files.delete.side_effect = BadRequestError(
+        message="File not found", response=mock_response, body=None
+    )
+    mock_lsc = mocker.patch(
+        "app.endpoints.vector_stores.AsyncLlamaStackClientHolder.get_client"
+    )
+    mock_lsc.return_value = mock_client
+    mocker.patch("app.endpoints.vector_stores.configuration", cfg)
+
+    request = get_test_request()
+    auth = get_test_auth()
+
+    with pytest.raises(HTTPException) as e:
+        await delete_vector_store_file(
+            request=request, vector_store_id="vs_123", file_id="file_999", auth=auth
+        )
+    assert e.value.status_code == status.HTTP_404_NOT_FOUND
diff --git a/tests/unit/app/test_routers.py b/tests/unit/app/test_routers.py
index 7ac178881..7e1f455a6 100644
--- a/tests/unit/app/test_routers.py
+++ b/tests/unit/app/test_routers.py
@@ -28,6 +28,7 @@
     stream_interrupt,
     streaming_query,
     tools,
+    vector_stores,
 )
 from app.routers import include_routers
 
@@ -109,7 +110,7 @@ def test_include_routers() -> None:
     include_routers(app)
 
     # are all routers added?
-    assert len(app.routers) == 22
+    assert len(app.routers) == 23
     assert root.router in app.get_routers()
     assert info.router in app.get_routers()
     assert models.router in app.get_routers()
@@ -132,6 +133,7 @@ def test_include_routers() -> None:
     assert a2a.router in app.get_routers()
     assert stream_interrupt.router in app.get_routers()
     assert responses.router in app.get_routers()
+    assert vector_stores.router in app.get_routers()
 
 
 def test_check_prefixes() -> None:
@@ -139,7 +141,7 @@ def test_check_prefixes() -> None:
 
     Verify that include_routers registers the expected routers with their configured URL prefixes.
 
-    Asserts that 21 routers are registered on a MockFastAPI instance and that
+    Asserts that 23 routers are registered on a MockFastAPI instance and that
     each router's prefix matches the expected value (e.g., root, health,
     authorized, metrics use an empty prefix; most API routers use "/v1";
     conversations_v2 uses "/v2").
@@ -148,7 +150,7 @@ def test_check_prefixes() -> None:
     include_routers(app)
 
     # are all routers added?
-    assert len(app.routers) == 22
+    assert len(app.routers) == 23
     assert app.get_router_prefix(root.router) == ""
     assert app.get_router_prefix(info.router) == "/v1"
     assert app.get_router_prefix(models.router) == "/v1"
@@ -172,3 +174,4 @@ def test_check_prefixes() -> None:
     assert app.get_router_prefix(a2a.router) == ""
     assert app.get_router_prefix(stream_interrupt.router) == "/v1"
     assert app.get_router_prefix(responses.router) == "/v1"
+    assert app.get_router_prefix(vector_stores.router) == "/v1"
diff --git a/tests/unit/models/requests/test_vector_store_requests.py b/tests/unit/models/requests/test_vector_store_requests.py
new file mode 100644
index 000000000..c666023f7
--- /dev/null
+++ b/tests/unit/models/requests/test_vector_store_requests.py
@@ -0,0 +1,158 @@
+"""Unit tests for Vector Store request models."""
+
+import pytest
+from pydantic import ValidationError
+
+from models.requests import VectorStoreFileCreateRequest, VectorStoreUpdateRequest
+
+
+class TestVectorStoreUpdateRequest:
+    """Test cases for the VectorStoreUpdateRequest model."""
+
+    def test_valid_update_with_name(self) -> None:
+        """Test valid update request with name field."""
+        request = VectorStoreUpdateRequest(name="updated_store")
+        assert request.name == "updated_store"
+        assert request.expires_at is None
+        assert request.metadata is None
+
+    def test_valid_update_with_expires_at(self) -> None:
+        """Test valid update request with expires_at field."""
+        request = VectorStoreUpdateRequest(expires_at=1735689600)
+        assert request.name is None
+        assert request.expires_at == 1735689600
+        assert request.metadata is None
+
+    def test_valid_update_with_metadata(self) -> None:
+        """Test valid update request with metadata field."""
+        metadata = {"user_id": "user123"}
+        request = VectorStoreUpdateRequest(metadata=metadata)
+        assert request.name is None
+        assert request.expires_at is None
+        assert request.metadata == metadata
+
+    def test_valid_update_with_multiple_fields(self) -> None:
+        """Test valid update request with multiple fields."""
+        request = VectorStoreUpdateRequest(
+            name="updated_store",
+            expires_at=1735689600,
+            metadata={"user_id": "user123"},
+        )
+        assert request.name == "updated_store"
+        assert request.expires_at == 1735689600
+        assert request.metadata == {"user_id": "user123"}
+
+    def test_empty_update_rejected(self) -> None:
+        """Test that empty update request is rejected."""
+        with pytest.raises(
+            ValueError,
+            match="At least one field must be provided: name, expires_at, or metadata",
+        ):
+            VectorStoreUpdateRequest()
+
+
+class TestVectorStoreFileCreateRequest:
+    """Test cases for the VectorStoreFileCreateRequest model."""
+
+    def test_valid_request_basic(self) -> None:
+        """Test valid request with only file_id."""
+        request = VectorStoreFileCreateRequest(file_id="file-abc123")
+        assert request.file_id == "file-abc123"
+        assert request.attributes is None
+        assert request.chunking_strategy is None
+
+    def test_valid_attributes_basic(self) -> None:
+        """Test valid request with attributes."""
+        attributes = {"key1": "value1", "key2": "value2"}
+        request = VectorStoreFileCreateRequest(
+            file_id="file-abc123", attributes=attributes
+        )
+        assert request.file_id == "file-abc123"
+        assert request.attributes == attributes
+
+    def test_attributes_max_16_pairs(self) -> None:
+        """Test that attributes can have exactly 16 pairs."""
+        attributes = {f"key{i}": f"value{i}" for i in range(16)}
+        request = VectorStoreFileCreateRequest(
+            file_id="file-abc123", attributes=attributes
+        )
+        assert len(request.attributes) == 16  # type: ignore
+
+    def test_attributes_exceeds_16_pairs(self) -> None:
+        """Test that attributes with more than 16 pairs is rejected."""
+        attributes = {f"key{i}": f"value{i}" for i in range(17)}
+        with pytest.raises(
+            ValueError, match="attributes can have at most 16 pairs, got 17"
+        ):
+            VectorStoreFileCreateRequest(file_id="file-abc123", attributes=attributes)
+
+    def test_attributes_key_max_64_chars(self) -> None:
+        """Test that attribute keys can be exactly 64 characters."""
+        key_64_chars = "a" * 64
+        attributes = {key_64_chars: "value"}
+        request = VectorStoreFileCreateRequest(
+            file_id="file-abc123", attributes=attributes
+        )
+        assert key_64_chars in request.attributes  # type: ignore
+
+    def test_attributes_key_exceeds_64_chars(self) -> None:
+        """Test that attribute keys exceeding 64 characters are rejected."""
+        key_65_chars = "a" * 65
+        attributes = {key_65_chars: "value"}
+        with pytest.raises(ValueError, match="exceeds 64 characters"):
+            VectorStoreFileCreateRequest(file_id="file-abc123", attributes=attributes)
+
+    def test_attributes_string_value_max_512_chars(self) -> None:
+        """Test that string attribute values can be exactly 512 characters."""
+        value_512_chars = "b" * 512
+        attributes = {"key": value_512_chars}
+        request = VectorStoreFileCreateRequest(
+            file_id="file-abc123", attributes=attributes
+        )
+        assert request.attributes["key"] == value_512_chars  # type: ignore
+
+    def test_attributes_string_value_exceeds_512_chars(self) -> None:
+        """Test that string attribute values exceeding 512 characters are rejected."""
+        value_513_chars = "b" * 513
+        attributes = {"key": value_513_chars}
+        with pytest.raises(ValueError, match="exceeds 512 characters"):
+            VectorStoreFileCreateRequest(file_id="file-abc123", attributes=attributes)
+
+    def test_attributes_non_string_values_allowed(self) -> None:
+        """Test that non-string attribute values (numbers, booleans) are not length-checked."""
+        attributes = {
+            "bool_key": True,
+            "int_key": 12345,
+            "float_key": 3.14159,
+        }
+        request = VectorStoreFileCreateRequest(
+            file_id="file-abc123", attributes=attributes
+        )
+        assert request.attributes == attributes
+
+    def test_attributes_mixed_value_types(self) -> None:
+        """Test that mixed value types in attributes are validated correctly."""
+        attributes = {
+            "string_key": "value",
+            "bool_key": False,
+            "number_key": 42,
+        }
+        request = VectorStoreFileCreateRequest(
+            file_id="file-abc123", attributes=attributes
+        )
+        assert request.attributes == attributes
+
+    def test_attributes_none_is_valid(self) -> None:
+        """Test that None attributes is valid (optional field)."""
+        request = VectorStoreFileCreateRequest(file_id="file-abc123", attributes=None)
+        assert request.attributes is None
+
+    def test_file_id_required(self) -> None:
+        """Test that file_id is required."""
+        with pytest.raises(ValidationError):
+            VectorStoreFileCreateRequest()  # type: ignore
+
+    def test_file_id_cannot_be_empty(self) -> None:
+        """Test that file_id cannot be an empty string."""
+        with pytest.raises(ValidationError, match="at least 1 character"):
+            VectorStoreFileCreateRequest(file_id="")
diff --git a/tests/unit/models/responses/test_error_responses.py b/tests/unit/models/responses/test_error_responses.py
index 306daeb6d..f3661f66f 100644
--- a/tests/unit/models/responses/test_error_responses.py
+++ b/tests/unit/models/responses/test_error_responses.py
@@ -81,7 +81,7 @@ def test_openapi_response(self) -> None:
 
         # Verify example count matches schema examples count
         assert len(examples) == expected_count
-        assert expected_count == 1
+        assert expected_count == 2
 
         # Verify example structure
         assert "conversation_id" in examples
@@ -472,7 +472,7 @@ def test_openapi_response(self) -> None:
 
         # Verify example count matches schema examples count
         assert len(examples) == expected_count
-        assert expected_count == 6
+        assert expected_count == 8
 
         # Verify all labeled examples are present
         assert "conversation" in examples
@@ -481,6 +481,8 @@ def test_openapi_response(self) -> None:
         assert "rag" in examples
         assert "streaming request" in examples
         assert "mcp server" in examples
+        assert "vector store" in examples
+        assert "file" in examples
 
         # Verify example structure for one example
         conversation_example = examples["conversation"]
diff --git a/tests/unit/observability/formats/test_rlsapi.py b/tests/unit/observability/formats/test_rlsapi.py
index 4ea8624ac..a88d62bdb 100644
--- a/tests/unit/observability/formats/test_rlsapi.py
+++ b/tests/unit/observability/formats/test_rlsapi.py
@@ -17,7 +17,7 @@ def sample_event_data_fixture() -> InferenceEventData:
         org_id="12345678",
         system_id="abc-def-123",
         request_id="req_xyz789",
-        cla_version="CLA/0.5.0",
+        cla_version="CLA/0.5.1",
         system_os="RHEL",
         system_version="9.3",
         system_arch="x86_64",
@@ -40,7 +40,7 @@ def test_builds_event_with_all_fields(
     assert event["org_id"] == "12345678"
     assert event["system_id"] == "abc-def-123"
     assert event["request_id"] == "req_xyz789"
-    assert event["cla_version"] == "CLA/0.5.0"
+    assert event["cla_version"] == "CLA/0.5.1"
     assert event["system_os"] == "RHEL"
     assert event["system_version"] == "9.3"
     assert event["system_arch"] == "x86_64"
diff --git a/uv.lock b/uv.lock
index cf3907f2d..2764d778d 100644
--- a/uv.lock
+++ b/uv.lock
@@ -39,8 +39,8 @@ dependencies = [
     { name = "psutil" },
     { name = "pyyaml" },
     { name = "safetensors" },
-    { name = "torch", version = "2.9.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
-    { name = "torch", version = "2.9.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
+    { name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
+    { name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" }
 wheels = [
@@ -470,15 +470,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/52/93/342cc62a70ab727e093ed98e02a725d85b746345f05d2b5e5034649f4ec8/chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443", size = 11595, upload-time = "2021-01-02T22:47:57.847Z" },
 ]
 
-[[package]]
-name = "circuitbreaker"
-version = "2.1.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/df/ac/de7a92c4ed39cba31fe5ad9203b76a25ca67c530797f6bb420fff5f65ccb/circuitbreaker-2.1.3.tar.gz", hash = "sha256:1a4baee510f7bea3c91b194dcce7c07805fe96c4423ed5594b75af438531d084", size = 10787, upload-time = "2025-03-31T08:12:08.963Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/ae/34/15f08edd4628f65217de1fc3c1a27c82e46fe357d60c217fc9881e12ebcc/circuitbreaker-2.1.3-py3-none-any.whl", hash = "sha256:87ba6a3ed03fdc7032bc175561c2b04d52ade9d5faf94ca2b035fbdc5e6b1dd1", size = 7737, upload-time = "2025-03-31T08:12:07.802Z" },
-]
-
 [[package]]
 name = "click"
 version = "8.3.1"
@@ -1620,8 +1611,8 @@ llslibdev = [
     { name = "pythainlp" },
     { name = "requests" },
     { name = "sentence-transformers" },
-    { name = "torch", version = "2.9.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
-    { name = "torch", version = "2.9.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
+    { name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
+    { name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
     { name = "transformers" },
     { name = "tree-sitter" },
     { name = "trl" },
@@ -1643,10 +1634,10 @@ requires-dist = [
     { name = "jinja2", specifier = ">=3.1.0" },
     { name = "jsonpath-ng", specifier = ">=1.6.1" },
     { name = "kubernetes", specifier = ">=30.1.0" },
-    { name = "litellm", specifier = ">=1.75.5.post1,<=1.82.6" },
-    { name = "llama-stack", specifier = "==0.5.2" },
-    { name = "llama-stack-api", specifier = "==0.5.2" },
-    { name = "llama-stack-client", specifier = "==0.5.2" },
+    { name = "litellm", specifier = ">=1.75.5.post1,<=1.84.0" },
+    { name = "llama-stack", specifier = "==0.5.4" },
+    { name = "llama-stack-api", specifier = "==0.5.4" },
+    { name = "llama-stack-client", specifier = "==0.5.4" },
     { name = "openai", specifier = ">=1.99.9" },
     { name = "prometheus-client", specifier = ">=0.22.1" },
     { name = "psycopg2-binary", specifier = ">=2.9.10" },
@@ -1706,7 +1697,7 @@ llslibdev = [
     { name = "google-cloud-aiplatform", specifier = ">=1.130.0" },
     { name = "httpx", specifier = ">=0.27.0" },
     { name = "langdetect", specifier = ">=1.0.9" },
-    { name = "litellm", specifier = ">=1.81.0,<=1.82.6" },
+    { name = "litellm", specifier = ">=1.81.0,<=1.84.0" },
     { name = "mcp", specifier = ">=1.23.0" },
     { name = "nltk", specifier = ">=3.8.1" },
     { name = "numpy", specifier = "==2.3.5" },
@@ -1721,7 +1712,7 @@ llslibdev = [
     { name = "pythainlp", specifier = ">=3.0.10" },
     { name = "requests", specifier = ">=2.33.0" },
     { name = "sentence-transformers", specifier = ">=5.0.0" },
-    { name = "torch", specifier = "==2.9.0", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "lightspeed-stack", group = "llslibdev" } },
+    { name = "torch", specifier = "==2.10.0", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "lightspeed-stack", group = "llslibdev" } },
     { name = "transformers", specifier = ">=4.34.0" },
     { name = "tree-sitter", specifier = ">=0.24.0" },
     { name = "trl", specifier = ">=0.18.2" },
@@ -1752,7 +1743,7 @@ wheels = [
 
 [[package]]
 name = "llama-stack"
-version = "0.5.2"
+version = "0.5.4"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "aiohttp" },
@@ -1766,13 +1757,10 @@ dependencies = [
     { name = "jsonschema" },
     { name = "llama-stack-api" },
     { name = "mcp" },
-    { name = "numpy" },
-    { name = "oci" },
     { name = "openai" },
     { name = "opentelemetry-distro" },
     { name = "opentelemetry-exporter-otlp-proto-http" },
     { name = "opentelemetry-sdk" },
-    { name = "oracledb" },
     { name = "pillow" },
     { name = "prompt-toolkit" },
     { name = "psycopg2-binary" },
@@ -1790,14 +1778,14 @@ dependencies = [
     { name = "urllib3" },
     { name = "uvicorn" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/ce/a8/3724d0c06a06578a639345f5086b93ba234a0ac247ec4ed7854d0d5e5ca6/llama_stack-0.5.2.tar.gz", hash = "sha256:9334c781e4ded6520aa60c3301a9087e9fb8fdaea8e5f30f8e21d85b17231d8d", size = 16035748, upload-time = "2026-03-06T13:25:59.356Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/0c/a5/cf7edd69b136f5e16b71f9765d4d62607ef3f535fa3750d9368e17d45a12/llama_stack-0.5.4.tar.gz", hash = "sha256:919df1fd4eb4e873420b43a6fce4b74e7ee762853b4627c73b70b0ee1a94c073", size = 16036849, upload-time = "2026-06-26T22:05:15.957Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/62/4c/fea3f2ffeead47a934704f1527685106766c5ea69dd99c0a83e872b22aa7/llama_stack-0.5.2-py3-none-any.whl", hash = "sha256:581fda638088ee029aab20afe3c42ba8f7f6ef21c80bd9ebcae20bb13c3409d3", size = 3979442, upload-time = "2026-03-06T13:25:56.581Z" },
+    { url = "https://files.pythonhosted.org/packages/eb/f8/83db8459ce7c9b3bb6de48b54e4adc43acf6fa14082ea7c22dd153863c46/llama_stack-0.5.4-py3-none-any.whl", hash = "sha256:709eb082c77db40e5ca1ed3e1a9d680ece310ea4f52638e4a771ed13676ba573", size = 3979555, upload-time = "2026-06-26T22:05:14.005Z" },
 ]
 
 [[package]]
 name = "llama-stack-api"
-version = "0.5.2"
+version = "0.5.4"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "fastapi" },
@@ -1807,14 +1795,14 @@ dependencies = [
     { name = "opentelemetry-sdk" },
     { name = "pydantic" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/b2/3d/ecc5cba3613a37887439f08bf202b455ad1d5411818c91833acfaaeee569/llama_stack_api-0.5.2.tar.gz", hash = "sha256:a272e4b803fe24a8ba7d22e6d904bf88abd118ba0b6610a20ff5dedb09f38ad7", size = 126436, upload-time = "2026-03-06T13:25:14.169Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/09/0d/ee848b4cbe728337d8e066d581932f5a632bb2bf0fed3086709725a837e0/llama_stack_api-0.5.4.tar.gz", hash = "sha256:8fec636ecec90b150507ca767d7a6f042df8b6c77acdaba7b397247f5bde3c70", size = 126444, upload-time = "2026-06-26T22:04:40.009Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/06/a7/caa050e0beb93147593766e8ea58a0aeab0de59d747ed74ec928c75ab113/llama_stack_api-0.5.2-py3-none-any.whl", hash = "sha256:6531556dd8bb6555d778360ecfcd850aad7a49a8172b68146995d538e71641f0", size = 151603, upload-time = "2026-03-06T13:25:12.876Z" },
+    { url = "https://files.pythonhosted.org/packages/79/9b/c3d6308bc0625ee3838953fc375cc2c2bc60a76cc3afd2dc16f7f1e35bc7/llama_stack_api-0.5.4-py3-none-any.whl", hash = "sha256:40fc7fbf901642831cf2ad83bc25013d7f2538463c1bd5ec1b43985c5d6ed90a", size = 151603, upload-time = "2026-06-26T22:04:38.533Z" },
 ]
 
 [[package]]
 name = "llama-stack-client"
-version = "0.5.2"
+version = "0.5.4"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "anyio" },
@@ -1833,9 +1821,9 @@ dependencies = [
     { name = "tqdm" },
     { name = "typing-extensions" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/99/8a/8742475db7cedc2d452a3a7677da7f24aa84bdd262bc97543029c62df772/llama_stack_client-0.5.2.tar.gz", hash = "sha256:17c1bbad90f7699da4eb3cae256e8823caa4d2be945512a45c8c6f89ab899f28", size = 368612, upload-time = "2026-03-06T13:24:22.252Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/41/f7/6378ae82adc9baf1f10f6a27279581b0fbb5e34df3be7448a840a2019bb1/llama_stack_client-0.5.4.tar.gz", hash = "sha256:89b274f46804f2e705d50716576dc6e4f63d15ae545b4eb2933f5caa490b4d8c", size = 368609, upload-time = "2026-06-26T22:03:51.514Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/4d/f9/f6224b8819748358a573e3a2b8e299c0b6ba5f9cedf2942188c361c8e555/llama_stack_client-0.5.2-py3-none-any.whl", hash = "sha256:473f4d67ac0b243b0fc29555a0203a742615d31bea606b4332d9e2f193f73d6a", size = 391951, upload-time = "2026-03-06T13:24:20.559Z" },
+    { url = "https://files.pythonhosted.org/packages/45/2e/4df65aa6b148a2f81409046206ba0fd2dadd8cbe555fe9f4e83b911c750e/llama_stack_client-0.5.4-py3-none-any.whl", hash = "sha256:6d910c51f917a9ab27b1ec5579fb9167184b9a3a5223abe5c34da123609caf7e", size = 391952, upload-time = "2026-06-26T22:03:49.718Z" },
 ]
 
 [[package]]
@@ -2244,24 +2232,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" },
 ]
 
-[[package]]
-name = "oci"
-version = "2.168.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
-    { name = "certifi" },
-    { name = "circuitbreaker" },
-    { name = "cryptography" },
-    { name = "pyopenssl" },
-    { name = "python-dateutil" },
-    { name = "pytz" },
-    { name = "urllib3" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/0b/d1/f7ad612e43390af57427150efe3b93d3f852983d4ba65fa4cb09898f8c9b/oci-2.168.3.tar.gz", hash = "sha256:098c7729a5e97e2bbd67dfb2218da355fbaff79a3e1c20cdbf137c79e97e1e1c", size = 16684155, upload-time = "2026-03-24T04:32:52.412Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/d7/30/7016a8bf2e02cb960974a4101dabb9da590540e584d446421375d72b5cf9/oci-2.168.3-py3-none-any.whl", hash = "sha256:370294488351dbb7cddc208bbbf64535e2b1f065e61f7f893de0ba1ac61c2d9a", size = 34129772, upload-time = "2026-03-24T04:32:44.178Z" },
-]
-
 [[package]]
 name = "openai"
 version = "2.30.0"
@@ -2437,28 +2407,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621, upload-time = "2026-03-04T14:17:19.33Z" },
 ]
 
-[[package]]
-name = "oracledb"
-version = "3.4.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
-    { name = "cryptography" },
-    { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f7/02/70a872d1a4a739b4f7371ab8d3d5ed8c6e57e142e2503531aafcb220893c/oracledb-3.4.2.tar.gz", hash = "sha256:46e0f2278ff1fe83fbc33a3b93c72d429323ec7eed47bc9484e217776cd437e5", size = 855467, upload-time = "2026-01-28T17:25:39.91Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/8f/81/2e6154f34b71cd93b4946c73ea13b69d54b8d45a5f6bbffe271793240d21/oracledb-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a7396664e592881225ba66385ee83ce339d864f39003d6e4ca31a894a7e7c552", size = 4220806, upload-time = "2026-01-28T17:26:04.322Z" },
-    { url = "https://files.pythonhosted.org/packages/ab/a9/a1d59aaac77d8f727156ec6a3b03399917c90b7da4f02d057f92e5601f56/oracledb-3.4.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f04a2d62073407672f114d02529921de0677c6883ed7c64d8d1a3c04caa3238", size = 2233795, upload-time = "2026-01-28T17:26:05.877Z" },
-    { url = "https://files.pythonhosted.org/packages/94/ec/8c4a38020cd251572bd406ddcbde98ca052ec94b5684f9aa9ef1ddfcc68c/oracledb-3.4.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8d75e4f879b908be66cce05ba6c05791a5dbb4a15e39abc01aa25c8a2492bd9", size = 2424756, upload-time = "2026-01-28T17:26:07.35Z" },
-    { url = "https://files.pythonhosted.org/packages/fa/7d/c251c2a8567151ccfcfbe3467ea9a60fb5480dc4719342e2e6b7a9679e5d/oracledb-3.4.2-cp312-cp312-win32.whl", hash = "sha256:31b7ee83c23d0439778303de8a675717f805f7e8edb5556d48c4d8343bcf14f5", size = 1453486, upload-time = "2026-01-28T17:26:08.869Z" },
-    { url = "https://files.pythonhosted.org/packages/4c/78/c939f3c16fb39400c4734d5a3340db5659ba4e9dce23032d7b33ccfd3fe5/oracledb-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:ac25a0448fc830fb7029ad50cd136cdbfcd06975d53967e269772cc5cb8c203a", size = 1794445, upload-time = "2026-01-28T17:26:10.66Z" },
-    { url = "https://files.pythonhosted.org/packages/22/68/f7126f5d911c295b57720c6b1a0609a5a2667b4546946433552a4de46333/oracledb-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:643c25d301a289a371e37fcedb59e5fa5e54fb321708e5c12821c4b55bdd8a4d", size = 4205176, upload-time = "2026-01-28T17:26:12.463Z" },
-    { url = "https://files.pythonhosted.org/packages/5d/93/2fced60f92dc82e66980a8a3ba5c1ea48110bf1dd81d030edb69d88f992e/oracledb-3.4.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55397e7eb43bb7017c03a981c736c25724182f5210951181dfe3fab0e5d457fb", size = 2231298, upload-time = "2026-01-28T17:26:14.497Z" },
-    { url = "https://files.pythonhosted.org/packages/75/a7/4dd286f3a6348d786fef9e6ab2e6c9b74ca9195d9a756f2a67e45743cdf0/oracledb-3.4.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26a10f9c790bd141ffc8af68520803ed4a44a9258bf7d1eea9bfdd36bd6df7f", size = 2439430, upload-time = "2026-01-28T17:26:16.044Z" },
-    { url = "https://files.pythonhosted.org/packages/19/28/94bc753e5e969c60ee5d9c914e2b4ef79999eaca8e91bcab2fbf0586b80b/oracledb-3.4.2-cp313-cp313-win32.whl", hash = "sha256:b974caec2c330c22bbe765705a5ac7d98ec3022811dec2042d561a3c65cb991b", size = 1458209, upload-time = "2026-01-28T17:26:17.652Z" },
-    { url = "https://files.pythonhosted.org/packages/cb/2b/593a9b2d4c12c9de3289e67d84fe023336d99f36ba51442a5a0f5ce6acf7/oracledb-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:3df8eee1410d25360599968b1625b000f10c5ae0e47274031a7842a9dc418890", size = 1793558, upload-time = "2026-01-28T17:26:19.914Z" },
-]
-
 [[package]]
 name = "packaging"
 version = "26.0"
@@ -2547,8 +2495,8 @@ dependencies = [
     { name = "psutil" },
     { name = "pyyaml" },
     { name = "safetensors" },
-    { name = "torch", version = "2.9.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
-    { name = "torch", version = "2.9.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
+    { name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
+    { name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
     { name = "tqdm" },
     { name = "transformers" },
 ]
@@ -3075,19 +3023,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" },
 ]
 
-[[package]]
-name = "pyopenssl"
-version = "25.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
-    { name = "cryptography" },
-    { name = "typing-extensions", marker = "python_full_version < '3.13'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/80/be/97b83a464498a79103036bc74d1038df4a7ef0e402cfaf4d5e113fb14759/pyopenssl-25.3.0.tar.gz", hash = "sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329", size = 184073, upload-time = "2025-09-17T00:32:21.037Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/d1/81/ef2b1dfd1862567d573a4fdbc9f969067621764fbb74338496840a1d2977/pyopenssl-25.3.0-py3-none-any.whl", hash = "sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6", size = 57268, upload-time = "2025-09-17T00:32:19.474Z" },
-]
-
 [[package]]
 name = "pyproject-hooks"
 version = "1.2.0"
@@ -3252,15 +3187,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" },
 ]
 
-[[package]]
-name = "pytz"
-version = "2026.1.post1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" },
-]
-
 [[package]]
 name = "pywin32"
 version = "311"
@@ -3660,8 +3586,8 @@ dependencies = [
     { name = "numpy" },
     { name = "scikit-learn" },
     { name = "scipy" },
-    { name = "torch", version = "2.9.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
-    { name = "torch", version = "2.9.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
+    { name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
+    { name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
     { name = "tqdm" },
     { name = "transformers" },
     { name = "typing-extensions" },
@@ -3898,7 +3824,7 @@ wheels = [
 
 [[package]]
 name = "torch"
-version = "2.9.0"
+version = "2.10.0"
 source = { registry = "https://download.pytorch.org/whl/cpu" }
 resolution-markers = [
     "python_full_version >= '3.13' and sys_platform == 'darwin'",
@@ -3914,14 +3840,18 @@ dependencies = [
     { name = "typing-extensions", marker = "sys_platform == 'darwin'" },
 ]
 wheels = [
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:4de0ed8cbc457a506dbca40376e206a29efee10756a00f1f3404bf67ad737d04" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:259548471194ab63d7ea273873053a6e3cc23530c1510f01e9d7ad259187bbd0" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e24836d968b54ef4dfb05594001a61958711ac9224026291e4e3f92f83a6fd7f" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:7fbbf409143a4fe0812a40c0b46a436030a7e1d14fe8c5234dfbe44df47f617e", upload-time = "2026-02-06T16:27:14Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:b39cafff7229699f9d6e172cac74d85fd71b568268e439e08d9c540e54732a3e", upload-time = "2026-02-06T16:27:17Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:358bd7125cbec6e692d60618a5eec7f55a51b29e3652a849fd42af021d818023", upload-time = "2026-02-10T19:55:42Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:470de4176007c2700735e003a830828a88d27129032a3add07291da07e2a94e8", upload-time = "2026-02-10T19:55:43Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:45a1c5057629444aeb1c452c18298fa7f30f2f7aeadd4dc41f9d340980294407", upload-time = "2026-01-23T15:09:55Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:339e05502b6c839db40e88720cb700f5a3b50cda332284873e851772d41b2c1e", upload-time = "2026-01-23T15:09:57Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:840351da59cedb7bcbc51981880050813c19ef6b898a7fecf73a3afc71aff3fe", upload-time = "2026-01-23T15:09:59Z" },
 ]
 
 [[package]]
 name = "torch"
-version = "2.9.0+cpu"
+version = "2.10.0+cpu"
 source = { registry = "https://download.pytorch.org/whl/cpu" }
 resolution-markers = [
     "python_full_version >= '3.13' and sys_platform == 'win32'",
@@ -3941,17 +3871,23 @@ dependencies = [
     { name = "typing-extensions", marker = "sys_platform != 'darwin'" },
 ]
 wheels = [
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3a651434ae1248b0568c12b5f9e3acc8942eb28378d9d04a79302938b68c6f24" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:28f6eb31b08180a5c5e98d5bc14eef6909c9f5a1dbff9632c3e02a8773449349" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:e438061b87ec7dd6018fca9f975219889aa0a3f6cdc3ea10dd0ae2bc7f1c47ce" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:eb13ff1c34e338d722e76a4fd83b8d282782505bd1b99af4b3c32da66eba6eb4" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:be4438d8dad7f0d5a5e54f0feef8a893446894ec87f102bb1d82dcc4518542e4" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6c9b217584400963d5b4daddb3711ec7a3778eab211e18654fba076cce3b8682" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:728372e3f58c5826445f677746e5311c1935c1a7c59599f73a49ded850e038e8" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:95e56c26f919fbb98f16e7a0b87af494b893f9da9a65a020f17a01c13e520a81" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:6c777160288b08555820781ae0f3a2c67a59bd24b065e88ca1ec20e2f9dc8ac7" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:528fd338311f31c9fb18038cafd00e6eae0bf5ad5577521701acb62510753d18" },
-    { url = "https://download.pytorch.org/whl/cpu/torch-2.9.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:d572863990e7d2762b547735ef589f6350d9eb4e441d38753a1c33636698cf4c" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-linux_aarch64.whl", hash = "sha256:8de5a36371b775e2d4881ed12cc7f2de400b1ad3d728aa74a281f649f87c9b8c", upload-time = "2026-01-23T15:10:22Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-linux_s390x.whl", hash = "sha256:9accc30b56cb6756d4a9d04fcb8ebc0bb68c7d55c1ed31a8657397d316d31596", upload-time = "2026-01-23T15:10:24Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:179451716487f8cb09b56459667fa1f5c4c0946c1e75fbeae77cfc40a5768d87", upload-time = "2026-01-23T15:10:25Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ee40b8a4b4b2cf0670c6fd4f35a7ef23871af956fecb238fbf5da15a72650b1d", upload-time = "2026-01-23T15:10:27Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:21cb5436978ef47c823b7a813ff0f8c2892e266cfe0f1d944879b5fba81bf4e1", upload-time = "2026-01-23T15:10:30Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:3eaa727e6a73affa61564d86b9d03191df45c8650d0666bd3d57c8597ef61e78", upload-time = "2026-01-23T15:10:31Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-linux_aarch64.whl", hash = "sha256:fd215f3d0f681905c5b56b0630a3d666900a37fcc3ca5b937f95275c66f9fd9c", upload-time = "2026-01-23T15:10:34Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:170a0623108055be5199370335cf9b41ba6875b3cb6f086db4aee583331a4899", upload-time = "2026-01-23T15:10:35Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e51994492cdb76edce29da88de3672a3022f9ef0ffd90345436948d4992be2c7", upload-time = "2026-01-23T15:10:37Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8d316e5bf121f1eab1147e49ad0511a9d92e4c45cc357d1ab0bee440da71a095", upload-time = "2026-01-23T15:10:38Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:b719da5af01b59126ac13eefd6ba3dd12d002dc0e8e79b8b365e55267a8189d3", upload-time = "2026-01-23T15:10:41Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:b67d91326e4ed9eccbd6b7d84ed7ffa43f93103aa3f0b24145f3001f3b11b714", upload-time = "2026-01-23T15:10:42Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-linux_aarch64.whl", hash = "sha256:5af75e5f49de21b0bdf7672bc27139bd285f9e8dbcabe2d617a2eb656514ac36", upload-time = "2026-01-23T15:10:44Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-linux_s390x.whl", hash = "sha256:ba51ef01a510baf8fff576174f702c47e1aa54389a9f1fba323bb1a5003ff0bf", upload-time = "2026-01-23T15:10:48Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0fedcb1a77e8f2aaf7bfd21591bf6d1e0b207473268c9be16b17cb7783253969", upload-time = "2026-01-23T15:10:48Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:106dd1930cb30a4a337366ba3f9b25318ebf940f51fd46f789281dd9e736bdc4", upload-time = "2026-01-23T15:10:50Z" },
+    { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:eb1bde1ce198f05c8770017de27e001d404499cf552aaaa014569eff56ca25c0", upload-time = "2026-01-23T15:10:50Z" },
 ]
 
 [[package]]