Skip to content
Merged
Show file tree
Hide file tree
Changes from 112 commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
54af7a6
feat(trtllm): support image inputs in sidecar
krishung5 Oct 15, 2026
f369b56
fix(trtllm): narrow image media cache identity
krishung5 Oct 15, 2026
f59ffd8
style(trtllm): tighten media comments
krishung5 Oct 15, 2026
9ef1535
test(trtllm): gate sidecar image requests in CI
krishung5 Oct 16, 2026
de12e02
feat(trtllm): pass video inputs through OpenEngine
krishung5 Oct 16, 2026
7ff6c3c
feat(trtllm): pass audio inputs through OpenEngine
krishung5 Oct 16, 2026
78b93ab
feat(trtllm): support image inputs in sidecar
krishung5 Oct 15, 2026
c7896b4
fix(trtllm): narrow image media cache identity
krishung5 Oct 15, 2026
bf4904f
style(trtllm): tighten media comments
krishung5 Oct 15, 2026
95b3a0f
test(trtllm): gate sidecar image requests in CI
krishung5 Oct 16, 2026
2e54556
fix(trtllm): address multimodal review findings
krishung5 Oct 16, 2026
33fad48
fix(ci): build sidecar image on compatible TRT-LLM base
krishung5 Oct 16, 2026
22e0e0d
test(trtllm): remove redundant image cases
krishung5 Oct 16, 2026
68dcee7
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 16, 2026
da67ad6
feat(trtllm): support image inputs in sidecar
krishung5 Oct 15, 2026
8705d4c
fix(trtllm): narrow image media cache identity
krishung5 Oct 15, 2026
7b55c8d
style(trtllm): tighten media comments
krishung5 Oct 15, 2026
16dd1f2
test(trtllm): gate sidecar image requests in CI
krishung5 Oct 16, 2026
e8c2e56
fix(trtllm): address multimodal review findings
krishung5 Oct 16, 2026
c47b92e
fix(ci): build sidecar image on compatible TRT-LLM base
krishung5 Oct 16, 2026
391fad4
test(trtllm): remove redundant image cases
krishung5 Oct 16, 2026
46eade0
feat(trtllm): pass video inputs through OpenEngine
krishung5 Oct 16, 2026
b5dafda
test(trtllm): trim redundant test narration
krishung5 Oct 19, 2026
1b95a36
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 19, 2026
17de5ad
ci(trtllm): refresh OpenEngine source pin
krishung5 Oct 19, 2026
3dc3d4b
Merge branch 'openengine-image-media-dynamo' into openengine-video-me…
krishung5 Oct 19, 2026
d9dee70
Merge remote-tracking branch 'origin/openengine-image-media-dynamo' i…
krishung5 Oct 19, 2026
480536f
fix(ci): include OpenEngine model config in source overlay
krishung5 Oct 19, 2026
ee80f78
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 19, 2026
4c13a3e
Merge branch 'openengine-image-media-dynamo' into openengine-video-me…
krishung5 Oct 19, 2026
343de4b
fix(trtllm): harden video media conversion
krishung5 Oct 19, 2026
1bce40a
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 19, 2026
a306889
feat(trtllm): pass audio inputs through OpenEngine
krishung5 Oct 20, 2026
c32a2a8
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 20, 2026
41c11d9
Merge branch 'openengine-image-media-dynamo' into openengine-video-me…
krishung5 Oct 20, 2026
cf267ea
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
09fb93c
test(trtllm): tighten video sidecar coverage
krishung5 Oct 20, 2026
8d4d843
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
4870a57
fix(trtllm): disable guided decoding explicitly for audio
krishung5 Oct 20, 2026
66bd6aa
ci(trtllm): refresh image source overlay
krishung5 Oct 20, 2026
c4b700e
Merge branch 'openengine-image-media-dynamo' into openengine-video-me…
krishung5 Oct 20, 2026
8cac783
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
8e14ae5
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Oct 20, 2026
70268bf
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
4b7c822
ci(trtllm): refresh video source overlay
krishung5 Oct 20, 2026
b489009
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
e2a8006
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 20, 2026
448a084
Merge branch 'openengine-image-media-dynamo' into openengine-video-me…
krishung5 Oct 20, 2026
b32b913
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
f783f9d
fix(trtllm): reject non-mapping engine configs
krishung5 Oct 20, 2026
6f49127
fix(trtllm): preserve text-only model prompts
krishung5 Oct 20, 2026
6310f0c
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 20, 2026
391c97c
Merge branch 'openengine-image-media-dynamo' into openengine-video-me…
krishung5 Oct 20, 2026
622f025
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 20, 2026
31d5b0d
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 21, 2026
612b948
fix(trtllm): address image sidecar discovery and overlay review
krishung5 Oct 21, 2026
9a810c9
fix(ci): package TRT-LLM media smoke helper with tests
krishung5 Oct 21, 2026
b2a0dfa
Merge latest image support into video branch
krishung5 Oct 21, 2026
cc234ed
fix(trtllm): clarify video ordering and decoder requirements
krishung5 Oct 21, 2026
3da37a0
Merge latest video support into audio branch
krishung5 Oct 21, 2026
6310877
Merge latest main into image sidecar branch
krishung5 Oct 22, 2026
9856530
Merge updated image branch into video sidecar branch
krishung5 Oct 22, 2026
106f4f3
ci(trtllm): pin reviewed video memory-budget fix
krishung5 Oct 22, 2026
9c7c628
Merge updated video branch into audio sidecar branch
krishung5 Oct 22, 2026
5b18885
ci(trtllm): pin reviewed audio decoding and capability fixes
krishung5 Oct 22, 2026
5c89098
Merge latest main into image sidecar branch
krishung5 Oct 22, 2026
5b8308d
Merge image sidecar branch into video stack
krishung5 Oct 22, 2026
50d2d24
perf(trtllm): move owned media URLs into OpenEngine requests
krishung5 Oct 22, 2026
98f6af1
ci(trtllm): carry video preparation support into the release overlay
krishung5 Oct 22, 2026
e9e811f
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Oct 23, 2026
e28f0b0
ci(trtllm): refresh OpenEngine image PR overlay
krishung5 Oct 26, 2026
b2793bd
test(trtllm): mark media overlay regressions for pre-merge
krishung5 Oct 26, 2026
03cd122
Merge remote-tracking branch 'origin/openengine-image-media-dynamo' i…
krishung5 Oct 26, 2026
f6b35a5
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Oct 26, 2026
2915cfd
fix(ci): validate video helpers through the shared media loader
krishung5 Oct 26, 2026
70373cd
test(trtllm): share video RPC coverage through the sidecar testkit
krishung5 Oct 26, 2026
85a693c
Merge remote-tracking branch 'origin/openengine-video-media-dynamo' i…
krishung5 Oct 26, 2026
5cecf61
Merge remote-tracking branch 'origin/main' into openengine-audio-medi…
krishung5 Oct 26, 2026
55f88d4
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 26, 2026
52a01cf
fix(ci): include KV event dependencies in TRT-LLM media overlay
krishung5 Oct 26, 2026
bd460ef
fix(ci): fail required TRT-LLM sidecar tests when OpenEngine is missing
krishung5 Oct 27, 2026
9e8157f
Split TRT-LLM image support from post-merge CI infrastructure
krishung5 Oct 27, 2026
1911e1c
Separate container E2E coverage from TRT-LLM image wiring
krishung5 Oct 27, 2026
93ea666
Merge latest main into TRT-LLM image wiring
krishung5 Oct 27, 2026
2609107
fix(trtllm): resolve sidecar image capabilities during startup
krishung5 Oct 28, 2026
d9c040c
ci(trtllm): test sidecar images against latest published nightly
krishung5 Oct 28, 2026
7de5066
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Oct 29, 2026
c6859f3
Merge remote-tracking branch 'origin/openengine-image-media-dynamo' i…
krishung5 Oct 29, 2026
e702d2e
ci(trtllm): keep nightly image GPU test out of PR checks
krishung5 Oct 29, 2026
789b719
Merge remote-tracking branch 'origin/main' into openengine-image-medi…
krishung5 Oct 29, 2026
c1669f0
Merge latest main into TRT-LLM image sidecar branch
krishung5 Oct 29, 2026
f9324e0
ci(trtllm): allow MPI workers in upstream nightly image test
krishung5 Oct 29, 2026
c2d5a21
ci(trtllm): shorten MPI hostname for upstream nightly test
krishung5 Oct 29, 2026
943e596
fix(trtllm): address nightly image CI review findings
krishung5 Oct 30, 2026
4ef0075
Merge latest main into TRT-LLM image sidecar branch
krishung5 Oct 30, 2026
5b7673a
chore(trtllm): align multinode examples with validated nightly
krishung5 Oct 30, 2026
467ef1e
Merge current image sidecar branch into video follow-up
krishung5 Oct 30, 2026
c498dd7
ci(trtllm): validate sidecar videos against upstream nightlies
krishung5 Oct 30, 2026
aac3636
Merge refreshed video sidecar branch into audio support
krishung5 Oct 30, 2026
78c6692
ci(trtllm): install optional decoder for sidecar video tests
krishung5 Oct 30, 2026
3711414
test(trtllm): share audio input capability RPC coverage
krishung5 Oct 30, 2026
ff4aae6
ci(trtllm): validate sidecar audio against upstream nightlies
krishung5 Oct 30, 2026
54bed09
Merge video decoder CI fix into audio sidecar branch
krishung5 Oct 30, 2026
d70754a
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Oct 30, 2026
a1f090a
test(trtllm): cover remote URL policy for video inputs
krishung5 Oct 30, 2026
7929df1
Merge remote-tracking branch 'origin/openengine-video-media-dynamo' i…
krishung5 Oct 30, 2026
805a010
Merge remote-tracking branch 'origin/main' into openengine-audio-medi…
krishung5 Oct 30, 2026
1ec1a63
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Oct 30, 2026
7c13542
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 30, 2026
3b11847
test(trtllm): cover remote URL policy for audio inputs
krishung5 Oct 30, 2026
19ae6c8
fix(trtllm): preserve terminal usage before hiding stop tokens
krishung5 Oct 30, 2026
7709f97
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Oct 30, 2026
024d807
fix(trtllm): preserve audio launch settings and validate usage
krishung5 Nov 2, 2026
c7d0df8
Merge remote-tracking branch 'origin/main' into openengine-video-medi…
krishung5 Nov 3, 2026
3e9a21e
Merge branch 'openengine-video-media-dynamo' into openengine-audio-me…
krishung5 Nov 3, 2026
7b08965
Merge remote-tracking branch 'origin/main' into openengine-audio-medi…
krishung5 Nov 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/filters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,9 @@ planner:
- '!**/*.rst'

sidecar_trtllm_image:
# PR CI runs resolver CPU tests; the GPU test runs independently in nightly CI.
# PR CI runs nightly resolver/harness CPU tests; GPU cases run in nightly CI.
- 'lib/sidecar/trtllm/ci/**'
- 'lib/llm/tests/data/media/triangle_240p_10.mp4'
- '.github/actions/changed-files/**'
- '.github/filters.yaml'
- '.github/workflows/pr.yaml'
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/test-filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ function checkFilter(file, patterns) {
// Test cases: [file, expectations, description]
// expectations: { filterName: expectedValue, ... }
const testCases = [
{
file: 'lib/llm/tests/data/media/triangle_240p_10.mp4',
expect: { sidecar_trtllm_image: true },
desc: 'the video fixture triggers the nightly harness checksum tests'
},
{
file: 'lib/sidecar/trtllm/ci/resolve_nightly.py',
expect: { sidecar_trtllm_image: true },
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/nightly-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ jobs:
sidecar_binary_artifact: ${{ needs.sidecar-build.outputs.binary_artifact }}
secrets: inherit

sidecar-trtllm-nightly-image-test:
sidecar-trtllm-nightly-media-test:
needs: [create-fresh-builder, compute-release-mode]
if: ${{ needs.compute-release-mode.outputs.run_tests == 'true' }}
uses: $/.github/workflows/shared-test-trtllm-nightly-image.yml
Expand Down Expand Up @@ -1748,7 +1748,7 @@ jobs:
name: Clean K8s builder if exists
runs-on: prod-default-small-v2
if: always()
needs: [vllm-build, sglang-build, trtllm-build, dynamo-pipeline, operator-build, planner-build, frontend-build, vllm-efa-build, sglang-efa-build, trtllm-efa-build, sidecar-build, sidecar-trtllm-nightly-image-test, create-fresh-builder]
needs: [vllm-build, sglang-build, trtllm-build, dynamo-pipeline, operator-build, planner-build, frontend-build, vllm-efa-build, sglang-efa-build, trtllm-efa-build, sidecar-build, sidecar-trtllm-nightly-media-test, create-fresh-builder]
permissions:
contents: read
steps:
Expand All @@ -1770,7 +1770,7 @@ jobs:
############################## SLACK NOTIFICATION ##############################
notify-slack:
if: always()
needs: [vllm-test, vllm-multi-gpu-test, vllm-4-gpu-test, vllm-h100-test, sglang-test, sglang-multi-gpu-test, sglang-4-gpu-test, sglang-h100-test, trtllm-test, trtllm-multi-gpu-test, trtllm-h100-test, sidecar-build, sidecar-vllm-test, sidecar-sglang-test, sidecar-trtllm-test, sidecar-trtllm-nightly-image-test, deploy-cleanup, deepseek-v4-pro-agg-perf, deepseek-v4-pro-disagg-perf, rust-tests, kimi-k25-agg-perf, kimi-k25-disagg-perf]
needs: [vllm-test, vllm-multi-gpu-test, vllm-4-gpu-test, vllm-h100-test, sglang-test, sglang-multi-gpu-test, sglang-4-gpu-test, sglang-h100-test, trtllm-test, trtllm-multi-gpu-test, trtllm-h100-test, sidecar-build, sidecar-vllm-test, sidecar-sglang-test, sidecar-trtllm-test, sidecar-trtllm-nightly-media-test, deploy-cleanup, deepseek-v4-pro-agg-perf, deepseek-v4-pro-disagg-perf, rust-tests, kimi-k25-agg-perf, kimi-k25-disagg-perf]
permissions:
contents: read
actions: read # grant the reusable notifier read access to list this run's jobs
Expand Down
24 changes: 17 additions & 7 deletions .github/workflows/shared-test-trtllm-nightly-image.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

name: TRT-LLM Sidecar Upstream Nightly Image
name: TRT-LLM Sidecar Upstream Nightly Media

on:
workflow_call:
Expand All @@ -19,14 +19,14 @@ permissions:

jobs:
build:
name: Build image integration client
name: Build media integration client
runs-on: prod-builder-v3
timeout-minutes: 60
outputs:
image: ${{ steps.config.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Test nightly selection
- name: Test nightly selection and media harness
run: python3 -m unittest discover -s lib/sidecar/trtllm/ci/tests -v
- name: Resolve latest upstream nightly
id: config
Expand Down Expand Up @@ -57,7 +57,11 @@ jobs:
retention-days: 7

test:
name: Upstream TRT-LLM image integration (1 GPU)
name: Upstream TRT-LLM ${{ matrix.media }} integration (1 GPU)
strategy:
fail-fast: false
matrix:
media: [image, video, audio]
Comment thread
krishung5 marked this conversation as resolved.
Resolved
needs: build
runs-on: [gpu-1, amd64, v2, us-west-2]
timeout-minutes: 30
Expand All @@ -84,18 +88,24 @@ jobs:
set -euo pipefail
source lib/sidecar/trtllm/launch/common.sh
trtllm_ensure_openengine_bindings python3
- name: Run live image integration test
- name: Install TRT-LLM video decoder
if: matrix.media == 'video'
run: >-
python3 components/src/dynamo/common/utils/install_media_decoders.py trtllm
- name: Run live ${{ matrix.media }} integration test
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: >-
python3 lib/sidecar/trtllm/ci/run_image_test.py
--binary trtllm-image-tests/trtllm-image-tests
--config trtllm-image-tests/nightly.json
--media ${{ matrix.media }}
--results test-results/trtllm-nightly-${{ matrix.media }}
- name: Upload engine logs and test results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: trtllm-nightly-image-results-${{ github.sha }}
path: test-results/trtllm-nightly-image/
name: trtllm-nightly-${{ matrix.media }}-results-${{ github.sha }}
path: test-results/trtllm-nightly-${{ matrix.media }}/
if-no-files-found: warn
retention-days: 7
3 changes: 2 additions & 1 deletion lib/backend-common/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ pub use dynamo_llm::protocols::common::llm_backend::{
};
pub use dynamo_llm::protocols::common::preprocessor::{
BootstrapInfo, KV_HINT_TRANSFER_CAPABILITY_KEY, KvHint, KvHintAction, KvSourceLocationsPayload,
MultimodalData, MultimodalDataMap, PrefillResult, PreprocessedRequest, RoutingHints,
MultimodalData, MultimodalDataMap, MultimodalItemRef, PrefillResult, PreprocessedRequest,
RoutingHints,
};
pub use dynamo_llm::protocols::common::{
FinishReason, GuidedDecodingOptions, OutputOptions, SamplingOptions, StopConditions,
Expand Down
4 changes: 2 additions & 2 deletions lib/backend-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ pub use engine::{
FinishReason, FirstTokenNotifier, GenerateContext, GuidedDecodingOptions, HEALTH_CHECK_KEY,
KV_HINT_TRANSFER_CAPABILITY_KEY, KvEventPublisher, KvEventSource, KvHint, KvHintAction,
KvSourceLocationsPayload, LLMEngine, LLMEngineOutput, LLMEngineOutputExt, LlmRegistration,
LogProbs, Metrics, MetricsBindings, MetricsCtx, MultimodalData, OnPublisherReady,
OnSnapshotPublisherReady, OutputOptions, PrefillResult, PreprocessedRequest,
LogProbs, Metrics, MetricsBindings, MetricsCtx, MultimodalData, MultimodalItemRef,
OnPublisherReady, OnSnapshotPublisherReady, OutputOptions, PrefillResult, PreprocessedRequest,
PromptTokensDetails, RawEngine, SamplingOptions, StopConditions, StopReason, TopLogprob,
TopLogprobs, chunk, usage,
};
Expand Down
63 changes: 62 additions & 1 deletion lib/llm/src/preprocessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ use crate::model_card::{ModelDeploymentCard, ModelInfo, PromptFormatterArtifact}
use crate::preprocessor::media::MediaFetcher;
use crate::preprocessor::media::{MediaDecoder, MediaLoader, max_data_url_bytes};
use crate::protocols::common::preprocessor::{
MultimodalData, MultimodalDataMap, MultimodalUuidMap, PreprocessedRequestBuilder, RoutingHints,
MultimodalData, MultimodalDataMap, MultimodalItemRef, MultimodalUuidMap,
PreprocessedRequestBuilder, RoutingHints,
};
use crate::protocols::common::timing::RequestTracker;
use crate::tokenizers::Encoding;
Expand Down Expand Up @@ -4111,6 +4112,7 @@ impl OpenAIPreprocessor {

let mut media_map: MultimodalDataMap = HashMap::new();
let mut uuid_map: MultimodalUuidMap = HashMap::new();
let mut media_order: Vec<MultimodalItemRef> = Vec::new();
let mut has_user_uuid = false;
// Decoded results are written back into these reserved modality slots so
// URL-backed and UUID-only inputs retain request order.
Expand Down Expand Up @@ -4190,6 +4192,10 @@ impl OpenAIPreprocessor {

let slots = media_map.entry(type_str.to_string()).or_default();
let slot_idx = slots.len();
media_order.push(MultimodalItemRef {
modality: type_str.to_string(),
index: slot_idx,
});
has_user_uuid |= uuid.is_some();
uuid_map
.entry(type_str.to_string())
Expand Down Expand Up @@ -4493,6 +4499,7 @@ impl OpenAIPreprocessor {

if !media_map.is_empty() {
builder.multi_modal_data(Some(media_map));
builder.multi_modal_order(Some(media_order));
if has_user_uuid {
builder.multi_modal_uuids(Some(uuid_map));
}
Expand Down Expand Up @@ -9286,6 +9293,60 @@ mod extra_args_media_copy_tests {
.to_string()
}

#[tokio::test]
async fn multimodal_order_tracks_the_rendered_media_sequence() {
let preprocessor = test_preprocessor();
let image = inline_data_url();
let request: NvCreateChatCompletionRequest = serde_json::from_value(serde_json::json!({
"model": "alias",
"messages": [{
"role": "user",
"content": [
{"type": "video_url", "video_url": {"url": "data:video/mp4;base64,AAAA"}},
{"type": "image_url", "image_url": {"url": image}},
{"type": "audio_url", "audio_url": {"url": "data:audio/wav;base64,AAAA"}},
{"type": "video_url", "video_url": {"url": "https://example.com/second.mp4"}}
]
}]
}))
.unwrap();
let mut builder = PreprocessedRequestBuilder::default();
builder
.model("alias".into())
.token_ids(Vec::new())
.stop_conditions(Default::default())
.sampling_options(Default::default())
.output_options(Default::default());

preprocessor
.gather_multi_modal_data_with_image_tokens(&request, &mut builder, None, &[])
.await
.unwrap();
let result = builder.build().unwrap();

assert_eq!(
result.multi_modal_order.unwrap(),
[
MultimodalItemRef {
modality: "video_url".to_string(),
index: 0,
},
MultimodalItemRef {
modality: "image_url".to_string(),
index: 0,
},
MultimodalItemRef {
modality: "audio_url".to_string(),
index: 0,
},
MultimodalItemRef {
modality: "video_url".to_string(),
index: 1,
},
]
);
}

#[tokio::test]
async fn deepseek_v41_display_name_fallback_orders_tool_media() {
use crate::common::checked_file::CheckedFile;
Expand Down
14 changes: 14 additions & 0 deletions lib/llm/src/protocols/common/preprocessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,13 @@ pub type MultimodalDataMap = std::collections::HashMap<String, Vec<MultimodalDat
/// Backend cache UUIDs aligned positionally with multimodal data slots.
pub type MultimodalUuidMap = std::collections::HashMap<String, Vec<Option<String>>>;

/// A reference into [`MultimodalDataMap`] preserving the media traversal order.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct MultimodalItemRef {
pub modality: String,
pub index: usize,
}

/// [`PreprocessedRequest`] is the internal representation of an LLM request. The `dynamo.llm-preprocessor`
/// crate is responsible for converting request from the public APIs to this internal representation.
#[derive(Serialize, Deserialize, Clone, Builder)]
Expand Down Expand Up @@ -443,6 +450,13 @@ pub struct PreprocessedRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub multi_modal_uuids: Option<MultimodalUuidMap>,

/// Media references in gather order: content parts, after the formatter's
/// message ordering. A chat template that regroups media by modality
/// renders a different placeholder order.
#[builder(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub multi_modal_order: Option<Vec<MultimodalItemRef>>,

/// Optional multimodal routing-only fields (separate from execution payload).
#[builder(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
Expand Down
26 changes: 8 additions & 18 deletions lib/sidecar/testkit/tests/support/trtllm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ pub struct Discovery {
pub calls: AtomicUsize,
pub unavailable: AtomicBool,
pub hold: AtomicBool,
pub portable_image: AtomicBool,
pub advertises_image: AtomicBool,
pub model_info: Mutex<Option<pb::ModelInfo>>,
pub omit_context_length: AtomicBool,
pub error: Mutex<Option<tonic::Code>>,
release: tokio::sync::Notify,
Expand Down Expand Up @@ -195,24 +194,15 @@ impl Control for ControlledControl {
if let Some(code) = *self.discovery.error.lock().unwrap() {
return Err(Status::new(code, "model discovery rejected"));
}
let mut response = self.inner.get_model_info(request).await?;
let info = response.get_mut();
if self.discovery.omit_context_length.load(Ordering::SeqCst) {
info.max_context_length = None;
}
info.supports_multimodal = Some(self.discovery.portable_image.load(Ordering::SeqCst));
let modalities = if self.discovery.advertises_image.load(Ordering::SeqCst) {
vec!["image"]
let model_info = self.discovery.model_info.lock().unwrap().clone();
let mut response = if let Some(info) = model_info {
Response::new(info)
} else {
vec![]
self.inner.get_model_info(request).await?
};
info.extra = Some(
dynamo_sidecar_common::json_to_struct(
serde_json::json!({"trtllm_supported_modalities": modalities}),
"test capabilities",
)
.unwrap(),
);
if self.discovery.omit_context_length.load(Ordering::SeqCst) {
response.get_mut().max_context_length = None;
}
Ok(response)
}

Expand Down
Loading
Loading