From 1f0c8ee3531241183e8eca67a1b9ce41235183ac Mon Sep 17 00:00:00 2001 From: Matthew Wittwer Date: Mon, 31 Aug 2026 16:21:22 +0000 Subject: [PATCH 1/4] bound the image fetch in the TRT-LLM preprocessing template --- .../trt_llm/preprocessing/1/model.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/triton_cli/templates/trt_llm/preprocessing/1/model.py b/src/triton_cli/templates/trt_llm/preprocessing/1/model.py index 8a38904..1e56a91 100644 --- a/src/triton_cli/templates/trt_llm/preprocessing/1/model.py +++ b/src/triton_cli/templates/trt_llm/preprocessing/1/model.py @@ -702,6 +702,12 @@ class VisionPreProcessor: in preparation for the vision encoder. """ + # Bounds applied when fetching an image over the network. A deployment + # that needs different limits should change these alongside the outbound + # network policy it enforces around the server. + IMAGE_FETCH_TIMEOUT_SECONDS = 5 + IMAGE_FETCH_MAX_BYTES = 32 * 1024 * 1024 + def __init__(self, vision_model_type, vision_model_processor, @@ -765,8 +771,22 @@ def load_images_from_urls(self, img_urls): image_buffer = io.BytesIO(image_data) images.append(Image.open(image_buffer)) else: - images.append( - Image.open(requests.get(img_url, stream=True).raw)) + # This URL comes from the inference request, so bound how long + # the fetch may stall and how much it may return. + response = requests.get( + img_url, + stream=True, + timeout=self.IMAGE_FETCH_TIMEOUT_SECONDS) + response.raise_for_status() + # Read one byte past the limit so that an oversized response is + # rejected rather than silently truncated. + image_data = response.raw.read(self.IMAGE_FETCH_MAX_BYTES + 1, + decode_content=True) + if len(image_data) > self.IMAGE_FETCH_MAX_BYTES: + raise ValueError( + f"[TensorRT-LLM][ERROR] Image at {img_url} exceeds the " + f"{self.IMAGE_FETCH_MAX_BYTES} byte limit.") + images.append(Image.open(io.BytesIO(image_data))) return images def mllama_process(self, queries, img_urls=None, image_bytes=None): From beb68304bd0a581a28e2cd7179547026d1bd779d Mon Sep 17 00:00:00 2001 From: Matthew Wittwer Date: Mon, 31 Aug 2026 16:45:42 +0000 Subject: [PATCH 2/4] add overall fetch timeout --- .../trt_llm/preprocessing/1/model.py | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/triton_cli/templates/trt_llm/preprocessing/1/model.py b/src/triton_cli/templates/trt_llm/preprocessing/1/model.py index 1e56a91..f4ab0bf 100644 --- a/src/triton_cli/templates/trt_llm/preprocessing/1/model.py +++ b/src/triton_cli/templates/trt_llm/preprocessing/1/model.py @@ -1,4 +1,4 @@ -# Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,6 +28,7 @@ import io import json import os +import time from typing import List import numpy as np @@ -702,10 +703,10 @@ class VisionPreProcessor: in preparation for the vision encoder. """ - # Bounds applied when fetching an image over the network. A deployment - # that needs different limits should change these alongside the outbound - # network policy it enforces around the server. - IMAGE_FETCH_TIMEOUT_SECONDS = 5 + # Bounds on fetching an image from a request-supplied URL. Adjust these + # together with the outbound network policy enforced around the server. + IMAGE_FETCH_TIMEOUT_SECONDS = 5 # no data received for this long + IMAGE_FETCH_MAX_SECONDS = 30 # entire transfer IMAGE_FETCH_MAX_BYTES = 32 * 1024 * 1024 def __init__(self, @@ -771,21 +772,24 @@ def load_images_from_urls(self, img_urls): image_buffer = io.BytesIO(image_data) images.append(Image.open(image_buffer)) else: - # This URL comes from the inference request, so bound how long - # the fetch may stall and how much it may return. response = requests.get( img_url, stream=True, timeout=self.IMAGE_FETCH_TIMEOUT_SECONDS) response.raise_for_status() - # Read one byte past the limit so that an oversized response is - # rejected rather than silently truncated. - image_data = response.raw.read(self.IMAGE_FETCH_MAX_BYTES + 1, - decode_content=True) - if len(image_data) > self.IMAGE_FETCH_MAX_BYTES: - raise ValueError( - f"[TensorRT-LLM][ERROR] Image at {img_url} exceeds the " - f"{self.IMAGE_FETCH_MAX_BYTES} byte limit.") + deadline = time.monotonic() + self.IMAGE_FETCH_MAX_SECONDS + image_data = bytearray() + for chunk in response.iter_content(chunk_size=65536): + image_data.extend(chunk) + if len(image_data) > self.IMAGE_FETCH_MAX_BYTES: + raise ValueError( + f"[TensorRT-LLM][ERROR] Image at {img_url} exceeds " + f"the {self.IMAGE_FETCH_MAX_BYTES} byte limit.") + if time.monotonic() > deadline: + raise ValueError( + f"[TensorRT-LLM][ERROR] Image at {img_url} took " + f"longer than {self.IMAGE_FETCH_MAX_SECONDS} " + "seconds to download.") images.append(Image.open(io.BytesIO(image_data))) return images From cbe37dcdffcbc62991e14f4c5efa8f444251de14 Mon Sep 17 00:00:00 2001 From: Matthew Wittwer Date: Fri, 11 Sep 2026 23:38:55 +0000 Subject: [PATCH 3/4] simplify chunking method in example --- .../trt_llm/preprocessing/1/model.py | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/triton_cli/templates/trt_llm/preprocessing/1/model.py b/src/triton_cli/templates/trt_llm/preprocessing/1/model.py index f4ab0bf..95959b8 100644 --- a/src/triton_cli/templates/trt_llm/preprocessing/1/model.py +++ b/src/triton_cli/templates/trt_llm/preprocessing/1/model.py @@ -28,7 +28,6 @@ import io import json import os -import time from typing import List import numpy as np @@ -706,7 +705,6 @@ class VisionPreProcessor: # Bounds on fetching an image from a request-supplied URL. Adjust these # together with the outbound network policy enforced around the server. IMAGE_FETCH_TIMEOUT_SECONDS = 5 # no data received for this long - IMAGE_FETCH_MAX_SECONDS = 30 # entire transfer IMAGE_FETCH_MAX_BYTES = 32 * 1024 * 1024 def __init__(self, @@ -772,24 +770,16 @@ def load_images_from_urls(self, img_urls): image_buffer = io.BytesIO(image_data) images.append(Image.open(image_buffer)) else: - response = requests.get( - img_url, - stream=True, - timeout=self.IMAGE_FETCH_TIMEOUT_SECONDS) - response.raise_for_status() - deadline = time.monotonic() + self.IMAGE_FETCH_MAX_SECONDS - image_data = bytearray() - for chunk in response.iter_content(chunk_size=65536): - image_data.extend(chunk) - if len(image_data) > self.IMAGE_FETCH_MAX_BYTES: - raise ValueError( - f"[TensorRT-LLM][ERROR] Image at {img_url} exceeds " - f"the {self.IMAGE_FETCH_MAX_BYTES} byte limit.") - if time.monotonic() > deadline: - raise ValueError( - f"[TensorRT-LLM][ERROR] Image at {img_url} took " - f"longer than {self.IMAGE_FETCH_MAX_SECONDS} " - "seconds to download.") + with requests.get( + img_url, + stream=True, + timeout=self.IMAGE_FETCH_TIMEOUT_SECONDS) as response: + response.raise_for_status() + image_data = bytearray() + for chunk in response.iter_content(chunk_size=65536): + if len(image_data) + len(chunk) > self.IMAGE_FETCH_MAX_BYTES: + raise ValueError("Image exceeds the download size limit.") + image_data.extend(chunk) images.append(Image.open(io.BytesIO(image_data))) return images From 5cf1e59b71550e02e0e144dc5b45faaec678bfd0 Mon Sep 17 00:00:00 2001 From: Matthew Wittwer Date: Fri, 11 Sep 2026 23:47:20 +0000 Subject: [PATCH 4/4] fix pre-commit python package issue --- .github/workflows/python-package.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yaml b/.github/workflows/python-package.yaml index 47e8451..32ec483 100644 --- a/.github/workflows/python-package.yaml +++ b/.github/workflows/python-package.yaml @@ -56,7 +56,7 @@ jobs: python -c "import triton_cli; print(triton_cli.__version__)" - name: Lint and format with ruff run: | - python3 -m pip install ruff + python3 -m pip install ruff==0.5.2 # keep in sync with .pre-commit-config.yaml ruff check . ruff format . - name: Test with pytest