diff --git a/pyproject.toml b/pyproject.toml index 483d9f5c0..698c27e7b 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.6.0", + "llama-stack-client==0.6.0", + "llama-stack-api==0.6.0", # Used by Logger "rich>=14.0.0", # Used by JWK token auth handler @@ -160,6 +160,7 @@ llslibdev = [ "faiss-cpu>=1.11.0", "chardet>=5.2.0", "psycopg2-binary>=2.9.10", + "pypdf>=6.9.2", # API scoring: inline::basic "requests>=2.33.0", # API datasetio: inline::localfs diff --git a/requirements-build.txt b/requirements-build.txt index 1297ea852..94b7bf891 100644 --- a/requirements-build.txt +++ b/requirements-build.txt @@ -16,6 +16,7 @@ flit-core==3.12.0 # jinja2 # packaging # pathspec + # pypdf # wheel hatch-fancy-pypi-readme==25.1.0 # via @@ -63,7 +64,7 @@ jinja2==3.1.6 # via uv-dynamic-versioning markupsafe==3.0.3 # via jinja2 -maturin==1.10.2 +maturin==1.12.6 # via fastuuid packaging==26.0 # via diff --git a/requirements.hashes.source.txt b/requirements.hashes.source.txt index 4efbb423d..07f2e56b4 100644 --- a/requirements.hashes.source.txt +++ b/requirements.hashes.source.txt @@ -509,15 +509,15 @@ langdetect==1.0.9 \ 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 +llama-stack==0.6.0 \ + --hash=sha256:b804830664dc91e54c7225a7a081cb1874c48fc18573569c19fac4a9397e8076 \ + --hash=sha256:d92711791633f5505a4473ffba3f3e26acb700716fddab5aec419d99e614c802 +llama-stack-api==0.6.0 \ + --hash=sha256:b99a03aba3659736b6b540c9e5e674b1daac2bf5eeb2a68795113d62b8250672 \ + --hash=sha256:f0f3a1a6239a5d3b8c7ef02cefdf817c96c6461dcd8a82c1689ac67ec3107270 +llama-stack-client==0.6.0 \ + --hash=sha256:3290aac36dcafbd1bc0baaf995522e2037f57056672b5a1516af112a4210f3ea \ + --hash=sha256:7e514a6ffd92f237aceb062dadc4db44e24a3cd9c4ea35e25173d1e0739beb8e markupsafe==3.0.3 \ --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ @@ -637,9 +637,9 @@ multiprocess==0.70.19 \ nltk==3.9.4 \ --hash=sha256:ed03bc098a40481310320808b2db712d95d13ca65b27372f8a403949c8b523d0 \ --hash=sha256:f2fa301c3a12718ce4a0e9305c5675299da5ad9e26068218b69d692fda84828f -oci==2.169.0 \ - --hash=sha256:c71bb5143f307791082b3e33cc1545c2490a518cfed85ab1948ef5107c36d30b \ - --hash=sha256:f3c5fff00b01783b5325ea7b13bf140053ec1e9f41da20bfb9c8a349ee7662fa +oci==2.170.0 \ + --hash=sha256:1e205a395e856b6514501d34595dd1e8e261415aaaab2c97f77fd2fc9618dcc3 \ + --hash=sha256:49adf0ffaf754c07c43194d19d2e11f5e1b602a95bb365bb384d939ff365b1b4 openai==2.30.0 \ --hash=sha256:92f7661c990bda4b22a941806c83eabe4896c3094465030dd882a71abe80c885 \ --hash=sha256:9a5ae616888eb2748ec5e0c5b955a51592e0b201a11f4262db920f2a78c5231d @@ -874,6 +874,9 @@ pyjwt==2.12.1 \ pyopenssl==26.0.0 \ --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pypdf==6.9.2 \ + --hash=sha256:662cf29bcb419a36a1365232449624ab40b7c2d0cfc28e54f42eeecd1fd7e844 \ + --hash=sha256:7f850faf2b0d4ab936582c05da32c52214c2b089d61a316627b5bfb5b0dab46c pythainlp==5.3.4 \ --hash=sha256:76744e51e27c895630bafd74f53a1f0aa8782cef2f7f02eebd6427fe8ce8d84d \ --hash=sha256:e66fd76fb5931834fd4e32ed54337ec62350d7654f187850e4dd4f915e9f624f diff --git a/requirements.hashes.wheel.txt b/requirements.hashes.wheel.txt index 095aba191..0222d3bb6 100644 --- a/requirements.hashes.wheel.txt +++ b/requirements.hashes.wheel.txt @@ -132,11 +132,6 @@ pandas==2.3.3 \ --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 \ diff --git a/src/app/endpoints/conversations_v1.py b/src/app/endpoints/conversations_v1.py index 42effa0ed..63a2826a7 100644 --- a/src/app/endpoints/conversations_v1.py +++ b/src/app/endpoints/conversations_v1.py @@ -3,6 +3,7 @@ from typing import Any from fastapi import APIRouter, Depends, HTTPException, Request +from llama_stack_api import ConversationNotFoundError from llama_stack_client import ( APIConnectionError, APIStatusError, @@ -275,7 +276,8 @@ async def get_conversation_endpoint_handler( # pylint: disable=too-many-locals, ).model_dump() raise HTTPException(**response) from e - except APIStatusError as e: + except (APIStatusError, ConversationNotFoundError) as e: + # In library mode, ConversationNotFoundError is raised instead of APIStatusError logger.error("Conversation not found: %s", e) response = NotFoundResponse( resource="conversation", resource_id=normalized_conv_id @@ -382,7 +384,8 @@ async def delete_conversation_endpoint_handler( response = ServiceUnavailableResponse(backend_name="Llama Stack", cause=str(e)) raise HTTPException(**response.model_dump()) from e - except APIStatusError: + except (APIStatusError, ConversationNotFoundError): + # In library mode, ConversationNotFoundError is raised instead of APIStatusError logger.warning( "Conversation %s in LlamaStack not found. Treating as already deleted.", normalized_conv_id, @@ -519,7 +522,8 @@ async def update_conversation_endpoint_handler( ).model_dump() raise HTTPException(**response) from e - except APIStatusError as e: + except (APIStatusError, ConversationNotFoundError) as e: + # In library mode, ConversationNotFoundError is raised instead of APIStatusError logger.error("Conversation not found: %s", e) response = NotFoundResponse( resource="conversation", resource_id=normalized_conv_id diff --git a/src/app/endpoints/query.py b/src/app/endpoints/query.py index f0578861c..748ca56e3 100644 --- a/src/app/endpoints/query.py +++ b/src/app/endpoints/query.py @@ -46,6 +46,7 @@ from utils.query import ( consume_query_tokens, handle_known_apistatus_errors, + is_context_length_error, prepare_input, store_query_results, update_azure_token, @@ -303,7 +304,7 @@ async def retrieve_response( response = cast(OpenAIResponseObject, response) except RuntimeError as e: # library mode wraps 413 into runtime error - if "context_length" in str(e).lower(): + if is_context_length_error(str(e)): error_response = PromptTooLongResponse(model=responses_params.model) raise HTTPException(**error_response.model_dump()) from e raise e diff --git a/src/app/endpoints/responses.py b/src/app/endpoints/responses.py index d70cab074..2c5dcb184 100644 --- a/src/app/endpoints/responses.py +++ b/src/app/endpoints/responses.py @@ -61,6 +61,7 @@ consume_query_tokens, extract_provider_and_model_from_model_id, handle_known_apistatus_errors, + is_context_length_error, store_query_results, update_azure_token, validate_model_provider_override, @@ -335,7 +336,7 @@ async def handle_streaming_response( inline_rag_context=inline_rag_context, ) except RuntimeError as e: # library mode wraps 413 into runtime error - if "context_length" in str(e).lower(): + if is_context_length_error(str(e)): error_response = PromptTooLongResponse(model=api_params.model) raise HTTPException(**error_response.model_dump()) from e raise e @@ -696,7 +697,7 @@ async def handle_non_streaming_response( ) except RuntimeError as e: - if "context_length" in str(e).lower(): + if is_context_length_error(str(e)): error_response = PromptTooLongResponse(model=api_params.model) raise HTTPException(**error_response.model_dump()) from e raise e diff --git a/src/app/endpoints/rlsapi_v1.py b/src/app/endpoints/rlsapi_v1.py index c78b00015..d7fc11a46 100644 --- a/src/app/endpoints/rlsapi_v1.py +++ b/src/app/endpoints/rlsapi_v1.py @@ -41,6 +41,7 @@ from utils.query import ( extract_provider_and_model_from_model_id, handle_known_apistatus_errors, + is_context_length_error, ) from utils.responses import ( build_turn_summary, @@ -531,8 +532,7 @@ def _map_inference_error_to_http_exception( # pylint: disable=too-many-return-s return HTTPException(**error_response.model_dump()) if isinstance(error, RuntimeError): - error_message = str(error).lower() - if "context_length" in error_message or "context length" in error_message: + if is_context_length_error(str(error)): logger.error("Prompt too long for request %s: %s", request_id, error) error_response = PromptTooLongResponse(model=model_id) return HTTPException(**error_response.model_dump()) diff --git a/src/app/endpoints/streaming_query.py b/src/app/endpoints/streaming_query.py index 8ec5bf380..6ed25e6fe 100644 --- a/src/app/endpoints/streaming_query.py +++ b/src/app/endpoints/streaming_query.py @@ -85,6 +85,7 @@ consume_query_tokens, extract_provider_and_model_from_model_id, handle_known_apistatus_errors, + is_context_length_error, prepare_input, store_query_results, update_azure_token, @@ -354,7 +355,7 @@ async def retrieve_response_generator( ) # Handle know LLS client errors only at stream creation time and shield execution except RuntimeError as e: # library mode wraps 413 into runtime error - if "context_length" in str(e).lower(): + if is_context_length_error(str(e)): error_response = PromptTooLongResponse(model=responses_params.model) raise HTTPException(**error_response.model_dump()) from e raise e @@ -590,7 +591,7 @@ async def generate_response( except RuntimeError as e: # library mode wraps 413 into runtime error error_response = ( PromptTooLongResponse(model=responses_params.model) - if "context_length" in str(e).lower() + if is_context_length_error(str(e)) else InternalServerErrorResponse.generic() ) yield stream_http_error_event(error_response, context.query_request.media_type) @@ -835,7 +836,7 @@ async def response_generator( # pylint: disable=too-many-branches,too-many-stat ) error_response = ( PromptTooLongResponse(model=context.model_id) - if "context_length" in error_message.lower() + if is_context_length_error(error_message) else InternalServerErrorResponse.query_failed(error_message) ) yield stream_http_error_event(error_response, media_type) diff --git a/src/constants.py b/src/constants.py index 5f05431c6..9325a6a38 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.6.0" UNABLE_TO_PROCESS_RESPONSE = "Unable to process this request" diff --git a/src/utils/query.py b/src/utils/query.py index 7c921a84d..0f4782712 100644 --- a/src/utils/query.py +++ b/src/utils/query.py @@ -51,6 +51,19 @@ logger = get_logger(__name__) +def is_context_length_error(error_message: str) -> bool: + """Check if an error message indicates a context length exceeded error. + + Args: + error_message: The error message to check. + + Returns: + True if the error indicates context length was exceeded. + """ + msg_lower = error_message.lower() + return "context_length" in msg_lower or "context length" in msg_lower + + def store_conversation_into_cache( user_id: str, conversation_id: str, @@ -578,10 +591,7 @@ def handle_known_apistatus_errors( """ if error.status_code == 400: error_message = getattr(error, "message", str(error)) - if ( - "context_length" in error_message.lower() - or "context length" in error_message.lower() - ): + if is_context_length_error(error_message): return PromptTooLongResponse(model=model_id) elif error.status_code == 429: return QuotaExceededResponse.model(model_id) diff --git a/tests/e2e/features/info.feature b/tests/e2e/features/info.feature index 8c1431790..8f269fdab 100644 --- a/tests/e2e/features/info.feature +++ b/tests/e2e/features/info.feature @@ -16,7 +16,7 @@ Feature: Info tests 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 llama-stack version 0.5.2 + And The body of the response has llama-stack version 0.6.0 @skip-in-library-mode Scenario: Check if info endpoint reports error when llama-stack connection is not working diff --git a/uv.lock b/uv.lock index 512385948..f468b3bb9 100644 --- a/uv.lock +++ b/uv.lock @@ -1618,6 +1618,7 @@ llslibdev = [ { name = "psutil" }, { name = "psycopg2-binary" }, { name = "pydantic" }, + { name = "pypdf" }, { name = "pythainlp" }, { name = "requests" }, { name = "sentence-transformers" }, @@ -1646,9 +1647,9 @@ requires-dist = [ { name = "jsonpath-ng", specifier = ">=1.6.1" }, { name = "kubernetes", specifier = ">=30.1.0" }, { name = "litellm", specifier = "==1.83.0" }, - { name = "llama-stack", specifier = "==0.5.2" }, - { name = "llama-stack-api", specifier = "==0.5.2" }, - { name = "llama-stack-client", specifier = "==0.5.2" }, + { name = "llama-stack", specifier = "==0.6.0" }, + { name = "llama-stack-api", specifier = "==0.6.0" }, + { name = "llama-stack-client", specifier = "==0.6.0" }, { name = "openai", specifier = ">=1.99.9" }, { name = "prometheus-client", specifier = ">=0.22.1" }, { name = "psycopg2-binary", specifier = ">=2.9.10" }, @@ -1720,6 +1721,7 @@ llslibdev = [ { name = "psutil", specifier = ">=7.0.0" }, { name = "psycopg2-binary", specifier = ">=2.9.10" }, { name = "pydantic", specifier = ">=2.10.6" }, + { name = "pypdf", specifier = ">=6.9.2" }, { name = "pythainlp", specifier = ">=3.0.10" }, { name = "requests", specifier = ">=2.33.0" }, { name = "sentence-transformers", specifier = ">=5.0.0" }, @@ -1754,7 +1756,7 @@ wheels = [ [[package]] name = "llama-stack" -version = "0.5.2" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1775,7 +1777,6 @@ dependencies = [ { name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-sdk" }, { name = "oracledb" }, - { name = "pillow" }, { name = "prompt-toolkit" }, { name = "psycopg2-binary" }, { name = "pydantic" }, @@ -1792,14 +1793,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/f4/53/5bc3ae19e9a42475b42682456898ced5a0b48e43f918920fc790b665f223/llama_stack-0.6.0.tar.gz", hash = "sha256:d92711791633f5505a4473ffba3f3e26acb700716fddab5aec419d99e614c802", size = 13631563, upload-time = "2026-03-11T15:06:13.071Z" } 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/9d/17/b4427e1db7409f698c95b6f8b2b9e662bbcf1f819beb1af180bab55ddfb5/llama_stack-0.6.0-py3-none-any.whl", hash = "sha256:b804830664dc91e54c7225a7a081cb1874c48fc18573569c19fac4a9397e8076", size = 770027, upload-time = "2026-03-11T15:06:10.649Z" }, ] [[package]] name = "llama-stack-api" -version = "0.5.2" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi" }, @@ -1809,14 +1810,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/f3/4f/0c6fbc861fb9f6074f877f9c39ea40b02ad1fc81c9a455b020b32dcc471f/llama_stack_api-0.6.0.tar.gz", hash = "sha256:f0f3a1a6239a5d3b8c7ef02cefdf817c96c6461dcd8a82c1689ac67ec3107270", size = 136402, upload-time = "2026-03-11T15:05:30.843Z" } 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/4d/44/3c7a8dc82ddcc45a375681051450979837f28b27250ee057cabcfb8421f3/llama_stack_api-0.6.0-py3-none-any.whl", hash = "sha256:b99a03aba3659736b6b540c9e5e674b1daac2bf5eeb2a68795113d62b8250672", size = 161069, upload-time = "2026-03-11T15:05:29.072Z" }, ] [[package]] name = "llama-stack-client" -version = "0.5.2" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1835,9 +1836,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/b7/e9/62dc71e7d6003d9b56a1e632445065f55687c891e62eff1636e10b5dd629/llama_stack_client-0.6.0.tar.gz", hash = "sha256:3290aac36dcafbd1bc0baaf995522e2037f57056672b5a1516af112a4210f3ea", size = 368695, upload-time = "2026-03-11T15:04:19.267Z" } 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/83/a3/33d3e066a320a993b6f9cca9c8efe8da7deb2045df61235d327d0a05b25f/llama_stack_client-0.6.0-py3-none-any.whl", hash = "sha256:7e514a6ffd92f237aceb062dadc4db44e24a3cd9c4ea35e25173d1e0739beb8e", size = 392001, upload-time = "2026-03-11T15:04:17.772Z" }, ] [[package]] @@ -2561,50 +2562,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/14/b4e3f574acf349ae6f61f9c000a77f97a3b315b4bb6ad03791e79ae4a568/peft-0.18.1-py3-none-any.whl", hash = "sha256:0bf06847a3551e3019fc58c440cffc9a6b73e6e2962c95b52e224f77bbdb50f1", size = 556960, upload-time = "2026-01-09T13:07:55.865Z" }, ] -[[package]] -name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, -] - [[package]] name = "pip" version = "24.3.1" @@ -3092,6 +3049,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/7d/d4f7d908fa8415571771b30669251d57c3cf313b36a856e6d7548ae01619/pyopenssl-26.0.0-py3-none-any.whl", hash = "sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81", size = 57969, upload-time = "2026-03-15T14:28:24.864Z" }, ] +[[package]] +name = "pypdf" +version = "6.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/83/691bdb309306232362503083cb15777491045dd54f45393a317dc7d8082f/pypdf-6.9.2.tar.gz", hash = "sha256:7f850faf2b0d4ab936582c05da32c52214c2b089d61a316627b5bfb5b0dab46c", size = 5311837, upload-time = "2026-03-23T14:53:27.983Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/7e/c85f41243086a8fe5d1baeba527cb26a1918158a565932b41e0f7c0b32e9/pypdf-6.9.2-py3-none-any.whl", hash = "sha256:662cf29bcb419a36a1365232449624ab40b7c2d0cfc28e54f42eeecd1fd7e844", size = 333744, upload-time = "2026-03-23T14:53:26.573Z" }, +] + [[package]] name = "pyproject-hooks" version = "1.2.0"