Skip to content

fix(common): make multimodal media loaders importable without torch - #11179

Open
waynehacking8 wants to merge 1 commit into
ai-dynamo:mainfrom
waynehacking8:wayne/fix-11172-lazy-multimodal-init
Open

waynehacking8 wants to merge 1 commit into
ai-dynamo:mainfrom
waynehacking8:wayne/fix-11172-lazy-multimodal-init

Conversation

@waynehacking8

@waynehacking8 waynehacking8 commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #11172.

from dynamo.common.multimodal import ImageLoader executed the package __init__.py eagerly, which pulled in torch through two independent paths:

  1. The chain reported in the issue: __init__.py -> async_encoder_cache -> dynamo.common.memory.multimodal_embedding_cache_manager -> import torch (plus embedding_transfer -> import torch / safetensors directly at embedding_transfer.py:17-19).
  2. A second path the issue did not name: audio_loader.py did a module-level best-effort from vllm.multimodal.media import ..., so in any environment where vllm is installed, importing the package dragged in the whole vllm/torch stack even when only ImageLoader was wanted.

Fix

  • __init__.py: keep the three media loaders eagerly importable (their footprint is PIL/numpy plus the local http helpers), and resolve the torch-dependent members lazily via a module-level __getattr__ (PEP 562): AsyncEncoderCache, the nine embedding-transfer names, and the two embedding factory dicts. This mirrors the existing house pattern in dynamo.common.configuration.__init__ and dynamo.common.__init__ (option 1 from the issue, least churn).
  • audio_loader.py: move the vllm import from module level into _require_vllm_audio_media(), matching the function-level pattern video_loader.py already uses (_require_vllm_video_media).

Public API is unchanged: every name in __all__ still resolves through plain from dynamo.common.multimodal import X; the factory dicts are built on first access, cached in module globals, and keep stable identity afterwards. The [gluo FIXME] note on the NIXL_READ receiver factory is preserved.

Validation

New CPU-only regression test components/src/dynamo/common/tests/multimodal/test_lazy_imports.py (markers gpu_0, pre_merge, unit). The torch-free assertions run in subprocesses so results are independent of what the pytest session already imported:

  • importing the package or any loader leaves torch and vllm out of sys.modules;
  • lazy members still resolve via from-imports and load torch only on first access;
  • factory dicts have stable identity and cover every EmbeddingTransferMode;
  • unknown attributes raise AttributeError.
$ PYTHONPATH=components/src python -m pytest components/src/dynamo/common/tests/multimodal/test_lazy_imports.py -q
5 passed in 4.15s

$ PYTHONPATH=components/src python -m pytest components/src/dynamo/common/tests/multimodal/ -q
6 failed, 74 passed in 6.24s

The 6 failures are TestNixlReadEmbeddingTransfer cases that fail identically on unmodified main in this environment (no NIXL library installed) — verified by stashing the change and rerunning.

isort/black/ruff clean on the three touched files.

Summary by CodeRabbit

  • New Features
    • Multimodal components now load more efficiently with lazy imports, reducing startup overhead.
    • Audio, image, and video loaders remain available without immediately loading heavier dependencies.
    • Embedding transfer helpers and factory mappings are now resolved only when first accessed.
  • Tests
    • Added coverage to verify lazy-loading behavior and ensure unknown attributes still raise clear errors.

Importing anything from dynamo.common.multimodal eagerly executed the
package __init__, which pulled in torch through two paths:

- async_encoder_cache -> memory.multimodal_embedding_cache_manager -> torch,
  and embedding_transfer -> torch/safetensors (the chain reported in ai-dynamo#11172);
- audio_loader's module-level best-effort vllm import, which drags in the
  whole vllm/torch stack whenever vllm is installed.

Resolve the torch-dependent members (AsyncEncoderCache, the
embedding-transfer classes, and the two embedding factory dicts) lazily via
PEP 562, mirroring dynamo.common.configuration's module __getattr__, and
move audio_loader's vllm import into _require_vllm_audio_media() to match
video_loader's existing function-level pattern. The media loaders stay
eagerly importable on their own dependency footprint (PIL/numpy/http).

Public API is unchanged: all names in __all__ still resolve via plain
from-imports, and the factory dicts keep stable identity after first access.

Fixes ai-dynamo#11172.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LfBBUwVVCtTK2TmUnPFHZf
Signed-off-by: WEI CHENG CHIU <waynehacking8@gmail.com>
@waynehacking8
waynehacking8 requested review from a team as code owners July 2, 2026 14:49
@copy-pr-bot

copy-pr-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@datadog-official

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

🚦 1 Pipeline job failed

PR Reminder Full CI Comment Bot | pr_reminder   View in Datadog   GitHub Actions

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: fcdde9e | Docs | Give us feedback!

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0e2fa5cc-b4a8-4a60-8712-d7d73ee03790

📥 Commits

Reviewing files that changed from the base of the PR and between d17cdf7 and fcdde9e.

📒 Files selected for processing (3)
  • components/src/dynamo/common/multimodal/__init__.py
  • components/src/dynamo/common/multimodal/audio_loader.py
  • components/src/dynamo/common/tests/multimodal/test_lazy_imports.py

Walkthrough

The multimodal package's __init__.py was refactored to lazily resolve embedding-transfer classes and factories via PEP 562 __getattr__ instead of eager imports, avoiding unconditional torch loading. audio_loader.py similarly moved its vllm import inline. New subprocess-based tests validate the lazy import behavior.

Changes

Lazy import refactor and validation

Layer / File(s) Summary
Lazy attribute resolution in multimodal __init__.py
components/src/dynamo/common/multimodal/__init__.py
Eager imports of embedding-transfer classes and factory wiring replaced with _EMBEDDING_TRANSFER_NAMES, _build_embedding_factories(), and a module-level __getattr__ that lazily imports/caches AsyncEncoderCache, embedding-transfer members, and factory dictionaries, raising AttributeError for unknown names.
Lazy vllm import in AudioLoader
components/src/dynamo/common/multimodal/audio_loader.py
Module-level vllm import replaced with a lazy import inside _require_vllm_audio_media(), catching ImportError and raising RuntimeError at call time instead of import time.
Import-hygiene test suite
components/src/dynamo/common/tests/multimodal/test_lazy_imports.py
New tests run in subprocesses verify that importing the package/loaders does not eagerly import torch/vllm, that lazy members trigger torch import only on access, that factory dicts resolve lazily with correct keys, and that invalid attributes raise AttributeError.

Estimated code review effort: 2 (Simple) | ~15 minutes

Related issues: Resolves #11172 (importing ImageLoader eagerly pulling in torch via __init__.py).

Suggested reviewers: none identified from provided context.

Poem:
A rabbit hopped past torch's weight,
Lazy loaders now dictate the fate,
No heavy stack till names are called,
Subprocess tests keep imports installed,
Hooray for imports light and svelte!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: making multimodal loaders importable without torch.
Description check ✅ Passed The PR description covers the fix, implementation, and validation, and includes the linked issue, though it doesn't mirror the template headings exactly.
Linked Issues check ✅ Passed The changes address #11172 by deferring heavy multimodal imports so ImageLoader can be imported without eagerly loading torch.
Out of Scope Changes check ✅ Passed The additional vllm import deferral and regression tests are directly related to the import-laziness goal and not out of scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands.

@glamr-agent

Copy link
Copy Markdown
Contributor

Template: review-only-pr
Engine: vllm
Review request: #11179

User intent

The caller wants issue #11172 fixed so that lightweight multimodal imports, especially from dynamo.common.multimodal import ImageLoader and access to the real validate_media_url path through media loaders, do not eagerly import torch or the broader ML stack. Heavy multimodal exports such as AsyncEncoderCache, embedding-transfer helpers, and embedding factory dictionaries should remain part of the package API but resolve lazily only when those names are actually used.

Discovery found an existing open GitHub PR that fully implements this requested behavior, so this work item should not author a duplicate fix. The remaining agency workflow should materialize and validate that existing PR's head diff under the review-only-pr contract.

Non-goals

  • Do not remove public re-exports from dynamo.common.multimodal or require callers to switch to submodule imports.
  • Do not make ImageLoader itself independent of its real lightweight dependencies such as PIL and the local HTTP/url-validator modules.
  • Do not change URL validation semantics, cache eviction behavior, NIXL media transfer behavior, or vLLM/TRT-LLM/SGLang runtime request handling.
  • Do not run Docker, Kubernetes, Slurm, remote sessions, or multi-GPU/disaggregated validation for this import-graph-only fix.
  • Do not publish or push from agency roles; publication is reserved for the publisher.

Discovery

I read the assigned brief at /sandbox/job/artifacts/wi-20260709T195040Z-11172/input.md, the sandbox capability report at /sandbox/job/artifacts/wi-20260709T195040Z-11172/compute-env.md, the planner role brief at /workflow/agents/01-agency/planner/goal.md, and the initially selected debug template at /workflow/agents/01-agency/templates/debug-investigation.md.

Relevant source files read in the checkout /home/sandbox/workspace/wi-20260709T195040Z-11172/repo:

  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/__init__.py eagerly imports AsyncEncoderCache, AudioLoader, embedding-transfer classes, ImageLoader, and VideoLoader; this makes package-level imports execute the heavy chain before the requested ImageLoader binding is reached.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/async_encoder_cache.py imports CachedEmbedding and MultimodalEmbeddingCacheManager at module import time.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/memory/multimodal_embedding_cache_manager.py imports torch at module import time and annotates cached tensors with torch.Tensor.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/image_loader.py imports PIL plus local HTTP/url-validation helpers and has no direct torch import.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/audio_loader.py currently performs a module-level best-effort vllm.multimodal.media import; in a vLLM image that can also pull the vLLM/torch stack during package import.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/video_loader.py already uses the safer function-level _require_vllm_video_media() pattern for vLLM media imports.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/__init__.py and /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/configuration/__init__.py already use module-level __getattr__ lazy import patterns that are appropriate precedent for this fix.

Existing tests discovered:

  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/multimodal/test_image_loader.py covers request deduplication, cancellation isolation, retry after failure, error normalization, URL-validation propagation, and LRU behavior, but imports ImageLoader from the concrete submodule and does not assert package import hygiene.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/http/test_url_validator.py covers validate_media_url behavior directly; it should not be duplicated for this import-graph fix.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/multimodal/test_audio_loader.py and test_video_loader.py cover loader behavior but not package-level lazy import behavior.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/multimodal/test_async_encoder_cache.py and /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/memory/test_multimodal_embedding_cache_manager.py exercise the heavy cache path where importing torch is expected.

Recent and related history checked:

Symptom and hypotheses

Symptom to reproduce: in the current main import graph, a subprocess that runs from dynamo.common.multimodal import ImageLoader in an environment with the vLLM image stack present sees torch and/or vllm loaded in sys.modules before it has used any embedding cache or media decode functionality.

Rank Hypothesis Distinguishing observation
1 dynamo.common.multimodal.__init__ eagerly imports AsyncEncoderCache, which imports the embedding cache manager, which imports torch. Block or inspect torch during from dynamo.common.multimodal import ImageLoader; the import reaches multimodal_embedding_cache_manager.py before ImageLoader is needed.
2 dynamo.common.multimodal.__init__ eagerly imports embedding-transfer exports and factory dictionaries that also belong to the torch-dependent embedding path. Accessing only ImageLoader should not load those names; accessing TransferRequest or factory dictionaries should still resolve later and may load torch.
3 AudioLoader's module-level vLLM import pulls in vLLM/torch even if encoder-cache exports are lazy. After making encoder-cache exports lazy, package import in a vLLM image still loads vllm unless audio_loader.py defers _require_vllm_audio_media() like video_loader.py already does.

PR #11179 addresses all three hypotheses and adds subprocess regression tests with negative controls, so it is the strongest existing implementation to reproduce and review.

Chosen approach

Adopt the review-only-pr contract for #11179 instead of producing a competing diff.

The printer should materialize PR #11179's head diff (fcdde9ebbbbb5daeaece3b79be2741d67804b50b) onto the configured working branch from the current checkout/base, preserving attribution and resolving only mechanical drift if current main has moved beyond the PR's recorded base SHA d17cdf71781e65b4a00750b08845e984e4a887ae. The materialized change should include:

  • Lazy PEP 562 __getattr__ resolution in dynamo.common.multimodal.__init__ for AsyncEncoderCache, embedding-transfer exports, and embedding sender/receiver factory dictionaries while keeping media loaders available at package level.
  • Function-local vLLM audio media imports in audio_loader.py, matching the existing video_loader.py pattern, so package import does not pull vLLM/torch in vLLM-equipped environments.
  • A new import-hygiene regression test, preferably components/src/dynamo/common/tests/multimodal/test_lazy_imports.py, that runs assertions in subprocesses to avoid contamination from pytest's already-imported modules.

Rejected alternatives

  • Author a fresh implementation on fix/lazy-torch-import-multimodal--a40c34c97c6e: rejected because PR fix(common): make multimodal media loaders importable without torch #11179 already implements the requested fix comprehensively and is open/mergeable.
  • Use PR fix(multimodal): lazy load package exports #11186 as the review target: rejected because it covers the primary AsyncEncoderCache/embedding path but, based on its changed-file list and description, does not include the separate audio_loader.py module-level vLLM import path that PR fix(common): make multimodal media loaders importable without torch #11179 identified and fixed.
  • Defer import torch inside multimodal_embedding_cache_manager.py: rejected for this work item because the lighter and less invasive package-level lazy export fix preserves the heavy cache module's internal typing/behavior while solving the reported ImageLoader import graph.
  • Drop package re-exports and require concrete submodule imports: rejected because it would break the public API used by existing callers such as from dynamo.common.multimodal import ImageLoader, TransferRequest, and embedding factory dictionaries.
  • Run vLLM aggregate smoke, disaggregated smoke, Docker build, Kubernetes envtest, or performance benchmarks: rejected because the behavioral claim is import hygiene and API preservation, not GPU runtime serving, image build correctness, operator behavior, or throughput.

Validation strategy

Validation must run only in the current sandbox through the recorder after the printer materializes PR #11179. The compute environment is vLLM with one A100, pytest available at /opt/dynamo/venv/bin/python -m pytest, pre-commit available, and Docker unavailable. No selected recipe requires Docker, Kubernetes, Slurm, or more than one GPU.

Planned evidence:

  1. 00-dynamo-editable-install: mandatory preflight because the planned pytest imports dynamo.*. The pre-agency setup already built dynamo._core, so the validator should record the idempotent confirmation, not improvise PYTHONPATH.
  2. 01-python-lint: record syntax compilation for changed Python files and run pre-commit on the materialized files if the hook environment is available. This is a lint pre-screen, not the sole behavioral proof.
  3. 03-python-unit-tests-mocker: record a subprocess import-hygiene regression. The key failing-before/passing-after behavior is that importing dynamo.common.multimodal or ImageLoader from the package leaves torch and vllm absent from sys.modules, while lazily accessing AsyncEncoderCache, TransferRequest, or factory dictionaries still resolves the public API. The negative controls should include stable factory identity and unknown attributes raising AttributeError. Run the new lazy-import test directly, and do not re-test URL validator semantics already covered by test_url_validator.py except as context. If feasible, record the before-proof under the same 03-python-unit-tests-mocker recipe by running the new regression against base/main with the production fix absent and expecting failure, then record the fixed run last so the recipe resolves green.
  4. 05-code-inspection: because this is a review-only-pr item, inspect the materialized diff against upstream PR fix(common): make multimodal media loaders importable without torch #11179, trace the import path through __init__.py, audio_loader.py, async_encoder_cache.py, multimodal_embedding_cache_manager.py, and the new test, and check for parallel eager imports in the dynamo.common.multimodal package. Treat upstream CI as context only.

Expected recipe block:

00-dynamo-editable-install
01-python-lint
03-python-unit-tests-mocker
05-code-inspection

07-agg-smoke is deliberately not selected even though one A100 and vLLM are available: a dummy model chat request would not prove the import-graph claim, and the change should be defended by CPU/import tests plus code inspection. 08-disagg-pair-smoke would be blocked on single GPU and is unrelated. 06-dockerfile-build is unavailable because Docker is not present and is unrelated. 09-gpu-pytest, 10-perf-benchmark, and 11-go-operator-tests are also not warranted.

Required deliverables

Under the review-only-pr workflow, the full agency run should produce:

  • /sandbox/job/artifacts/wi-20260709T195040Z-11172/plan.md from this planner.
  • /sandbox/job/artifacts/wi-20260709T195040Z-11172/change.md describing the materialized PR fix(common): make multimodal media loaders importable without torch #11179 diff and any mechanical drift resolution.
  • /sandbox/job/artifacts/wi-20260709T195040Z-11172/change.diff containing the nonempty materialized diff.
  • /sandbox/job/artifacts/wi-20260709T195040Z-11172/change-validation.md with recorded recipe evidence and a final validator verdict.
  • /sandbox/job/artifacts/wi-20260709T195040Z-11172/review.md assessing the materialized PR and local evidence.

Only the publisher may later comment on or publish to the configured review destination.

@glamr-agent

Copy link
Copy Markdown
Contributor

Change packet

Working branch: fix/lazy-torch-import-multimodal--a40c34c97c6e

Branch commit: 4b5a643dfb4f99988dd55baaae93463defaf2b9d

Materialized review request: #11179

Supplied PR head SHA: fcdde9ebbbbb5daeaece3b79be2741d67804b50b

Original author: WEI CHENG CHIU waynehacking8@gmail.com

Attribution preserved in the reproduced commit author and original commit trailer metadata, including Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> and Signed-off-by: WEI CHENG CHIU <waynehacking8@gmail.com>.

Summary

Materialized PR #11179's head diff onto the configured local branch from main using git cherry-pick fcdde9ebbbbb5daeaece3b79be2741d67804b50b. The resulting patch-id matches the supplied upstream PR head patch-id (0cf8d35d3b26f87622b73902e3a34c42375df165), so no source drift resolution beyond normal cherry-pick replay was required.

Files changed

  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/__init__.py: Keeps media loader re-exports available while moving AsyncEncoderCache, embedding transfer classes, and embedding factory dictionaries behind module-level lazy __getattr__ resolution. This preserves the public API while avoiding eager import of the torch-dependent embedding cache path during lightweight package imports.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/audio_loader.py: Moves the optional vllm.multimodal.media import into _require_vllm_audio_media(), matching the existing video-loader lazy pattern so importing the audio loader does not pull in vLLM/torch.
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/multimodal/test_lazy_imports.py: Adds subprocess-based import-hygiene regression coverage for package imports, package-level ImageLoader access, lazy heavy exports, stable factory identity, and unknown attribute behavior.

Build, lint, and tests

No build, lint, or pytest commands were run by the printer stage. Per the review-only-pr agency contract, this stage only materialized the supplied PR diff and emitted /sandbox/job/artifacts/wi-20260709T195040Z-11172/change.diff; planned validation is reserved for the validator packet.

Safety constraints inspected

  • Read /workflow/agents/01-agency/printer/goal.md, /sandbox/job/artifacts/wi-20260709T195040Z-11172/input.md, /sandbox/job/artifacts/wi-20260709T195040Z-11172/compute-env.md, /sandbox/job/artifacts/wi-20260709T195040Z-11172/plan.md, and /workflow/agents/01-agency/templates/review-only-pr.md.
  • Used only the configured branch name fix/lazy-torch-import-multimodal--a40c34c97c6e from GLAMR_CODING_BRANCH_NAME and based it on main from GLAMR_CODING_BASE_BRANCH.
  • Did not push, open a review request, or comment on the supplied PR.
  • Wrote only the required packets under /sandbox/job/artifacts/wi-20260709T195040Z-11172/.

@glamr-agent

Copy link
Copy Markdown
Contributor

Change validation

Hardware target: local sandbox only, vLLM validation image, one NVIDIA A100-SXM4-80GB, CUDA 12.9. No Docker, Kubernetes, Slurm, SSH, or remote compute pathway was used. The planned and supported recipes were 00-dynamo-editable-install, 01-python-lint, 03-python-unit-tests-mocker, and 05-code-inspection.

Recorded evidence

All recipe commands were run through /workflow/agents/customs/evidence.ts against /sandbox/job/artifacts/wi-20260709T195040Z-11172.

  • 00-dynamo-editable-install: first recorded attempt hit a stale/corrupt default cargo target artifact (/sandbox/job/.cargo-target/maturin/lib_core.so: malformed ELF) and exited 1. I reran the same recipe with a clean job-local CARGO_TARGET_DIR=/sandbox/job/artifacts/wi-20260709T195040Z-11172/.cargo-target-validation; dynamo-prepare-python-validation rebuilt successfully and python3 -c "import dynamo, dynamo._core" exited 0. Deciding green log: validation/logs/2026-07-09T20-10-48.387Z-bash-917a.log.
  • 01-python-lint: python3 -m py_compile passed for the three changed Python files, and pre-commit run --files ... --hook-stage manual passed isort, black, flake8, codespell, pre-commit hooks, ruff, and marker reporting. Deciding green log: validation/logs/2026-07-09T20-16-05.824Z-bash-0dbb.log.
  • 03-python-unit-tests-mocker: /opt/dynamo/venv/bin/python -m pytest components/src/dynamo/common/tests/multimodal/test_lazy_imports.py --tb=short -v collected and passed 5 tests. These subprocess tests exercise package import without torch/vllm, package-level loader imports without torch/vllm, lazy heavy member resolution with torch loading only on access, stable lazy factory dictionary identity, and unknown attribute AttributeError. Deciding green log: validation/logs/2026-07-09T20-16-59.182Z-bash-f88b.log.
  • 05-code-inspection: two preliminary recorded inspection attempts failed because the first used an unsupported gh pr view JSON field and the second treated a non-required pr_reminder failure as a hard check. The final recorded inspection run passed after checking required PR checks separately and treating all-check output as context. It confirmed the local materialized patch-id matches upstream PR fix(common): make multimodal media loaders importable without torch #11179 (0cf8d35d3b26f87622b73902e3a34c42375df165), changed files match the upstream PR, required checks (copyright-checks, pre-merge-status-check) pass, and there are zero CHANGES_REQUESTED reviews. Deciding green log: validation/logs/2026-07-09T20-18-26.972Z-bash-e9e4.log.

No planned recipe is blocked. Upstream CI is context only; local recorded evidence is the basis for this verdict.

Behavioral reading

The materialized diff matches upstream PR #11179 and changes exactly:

  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/__init__.py
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/audio_loader.py
  • /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/multimodal/test_lazy_imports.py

Source inspection supports the import-graph claim. ImageLoader remains an eager package-level re-export at components/src/dynamo/common/multimodal/__init__.py:14, while the heavy exports are resolved by module-level __getattr__ at components/src/dynamo/common/multimodal/__init__.py:83. Factory dictionaries are built lazily by _build_embedding_factories() at components/src/dynamo/common/multimodal/__init__.py:46. The prior heavy chain still exists when intentionally accessed: async_encoder_cache.py:22 imports the embedding cache manager, and multimodal_embedding_cache_manager.py:24 imports torch. That means the change defers, rather than removes, the torch-dependent path.

The audio loader no longer imports vLLM media classes at module import time. _require_vllm_audio_media() is defined at components/src/dynamo/common/multimodal/audio_loader.py:48, and the vLLM imports occur inside that helper at lines 51-52, so importing dynamo.common.multimodal does not immediately pull vLLM/torch through audio support. The parallel scan still found torch/vLLM imports in embedding/media helper modules and in video/audio helper functions; these are out-of-scope or intentionally lazy and do not contradict the package-import claim.

The new regression tests are load-bearing: test_package_import_does_not_import_torch and test_image_loader_import_does_not_import_torch assert the requested lightweight import behavior in clean subprocesses, while test_lazy_members_resolve_and_torch_loads_on_access and test_factory_dicts_resolve_lazily_with_stable_identity verify API preservation for heavy lazy exports.

Checklist self-check

  • Verdict heading is the final packet element and uses the exact accepted form.
  • The load-bearing test suite ran in the real repository pytest binary against the changed checkout.
  • The added subprocess helper is not standalone infrastructure; positive lightweight imports, heavy lazy access, stable factory identity, and unknown-attribute negative behavior were all exercised by the recorded pytest run.

Verdict: pass

@glamr-agent

Copy link
Copy Markdown
Contributor

🤖 Automated AI review — advisory. An AI agent's judgment of
whether this change is logically sound on the evidence; not a merge
authorization. CI, customs, and a human reviewer hold that.

Assessment: sound

Findings

No blocking or nit findings.

Evidence reviewed

  • Git/materialization state: the checkout is on fix/lazy-torch-import-multimodal--a40c34c97c6e at 4b5a643dfb4f99988dd55baaae93463defaf2b9d, matching the branch commit recorded in /sandbox/job/artifacts/wi-20260709T195040Z-11172/change.md; the branch is not main, and git status --short --branch showed no uncommitted edits at inspection time.
  • Upstream drift check: the materialized commit and immutable upstream PR fix(common): make multimodal media loaders importable without torch #11179 SHA fcdde9ebbbbb5daeaece3b79be2741d67804b50b have the same stable patch-id, 0cf8d35d3b26f87622b73902e3a34c42375df165; the upstream and local changed-file lists are the same three files. This supports the printer's claim that no accidental source drift was introduced while replaying the PR onto current main.
  • Import-graph logic: /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/__init__.py:13-15 keeps the media loaders available as package-level exports, while /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/__init__.py:83-98 defers AsyncEncoderCache, embedding-transfer exports, and factory dictionaries through module-level __getattr__. The existing heavy path still loads only on intentional access through /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/async_encoder_cache.py:22 and /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/memory/multimodal_embedding_cache_manager.py:24, so the fix defers rather than removes the torch-dependent API.
  • Audio-loader import hygiene: /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/multimodal/audio_loader.py:48-58 imports vLLM media classes inside _require_vllm_audio_media(), matching the intended lazy pattern and avoiding vLLM/torch during package import.
  • Validation evidence: /sandbox/job/artifacts/wi-20260709T195040Z-11172/.evidence-report-check.txt reports 4/4 validated for the planned recipes from plan.md: 00-dynamo-editable-install, 01-python-lint, 03-python-unit-tests-mocker, and 05-code-inspection. The deciding logs show actual execution and pass results: 2026-07-09T20-10-48.387Z-bash-917a.log rebuilt/confirmed the editable install and imported dynamo._core; 2026-07-09T20-16-05.824Z-bash-0dbb.log passed py_compile and pre-commit hooks; 2026-07-09T20-16-59.182Z-bash-f88b.log ran the new subprocess import-hygiene test file and passed all 5 tests; 2026-07-09T20-18-26.972Z-bash-e9e4.log confirmed patch-id/file parity with PR fix(common): make multimodal media loaders importable without torch #11179 and inspected the remaining eager import sites.
  • New test infrastructure exercised: /home/sandbox/workspace/wi-20260709T195040Z-11172/repo/components/src/dynamo/common/tests/multimodal/test_lazy_imports.py:24-33 adds the subprocess helper, and the recorded pytest log exercised the import-hygiene tests at lines 36, 46, 57, 71, and 87, not merely static registration.
  • Compute pathway: the validation packet states local sandbox-only execution with no Docker, Kubernetes, Slurm, SSH, or remote compute; this matches /sandbox/job/artifacts/wi-20260709T195040Z-11172/compute-env.md, and the selected recipes did not require multi-GPU or remote validation.

Assessment: sound

This branch was previously deployed

1 inactive deployment
external_collaborator — fcdde9eb Deployed Jul 2, 2026 by waynehacking8 via ok-to-test #1581
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Importing ImageLoader from dynamo.common.multimodal eagerly pulls in torch via __init__.py

2 participants