KEMBAR78
[TRTLLM-6683][feat] Support LoRA reload CPU cache evicted adapter by amitz-nv · Pull Request #6786 · NVIDIA/TensorRT-LLM · GitHub
Skip to content

Conversation

@amitz-nv
Copy link
Collaborator

@amitz-nv amitz-nv commented Aug 11, 2025

Description

Cherrypick of merge commit of #6510

Added support for reusing a LoRA adapter after it was evicted from LoRA CPU cache:

  • Added py_lora_path optional field to Request and to LlmRequest classes (only in python).
  • Python PeftCacheManager.add_request_peft would load the LoRA adapter if it's not loaded in cache.
  • Refactored LoRA parameterized test into separate test cases.
  • Added new LoRA test cases:
    • Test that using a LoRA adapter after it was evicted from LoRA CPU cache works.
    • Test that using LoRA adapters after they were inserted to LoRA cache work.
  • Removed "reuse after CPU eviction not supported" message from CPP exception, and the test case that verified the exception included that note.

Test Coverage

  • tests/unittest/llmapi/test_llm_pytorch.py::test_llama_7b_multi_lora_evict_and_reload_evicted_adapters_in_cpu_and_gpu_cache
  • tests/unittest/llmapi/test_llm_pytorch.py::test_llama_7b_multi_lora_read_from_cache_after_insert

GitHub Bot Help

/bot [-h] ['run', 'kill', 'skip', 'reuse-pipeline'] ...

Provide a user friendly way for developers to interact with a Jenkins server.

Run /bot [-h|--help] to print this help message.

See details below for each supported subcommand.

run [--reuse-test (optional)pipeline-id --disable-fail-fast --skip-test --stage-list "A10-PyTorch-1, xxx" --gpu-type "A30, H100_PCIe" --test-backend "pytorch, cpp" --add-multi-gpu-test --only-multi-gpu-test --disable-multi-gpu-test --post-merge --extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx" --detailed-log --debug(experimental)]

Launch build/test pipelines. All previously running jobs will be killed.

--reuse-test (optional)pipeline-id (OPTIONAL) : Allow the new pipeline to reuse build artifacts and skip successful test stages from a specified pipeline or the last pipeline if no pipeline-id is indicated. If the Git commit ID has changed, this option will be always ignored. The DEFAULT behavior of the bot is to reuse build artifacts and successful test results from the last pipeline.

--disable-reuse-test (OPTIONAL) : Explicitly prevent the pipeline from reusing build artifacts and skipping successful test stages from a previous pipeline. Ensure that all builds and tests are run regardless of previous successes.

--disable-fail-fast (OPTIONAL) : Disable fail fast on build/tests/infra failures.

--skip-test (OPTIONAL) : Skip all test stages, but still run build stages, package stages and sanity check stages. Note: Does NOT update GitHub check status.

--stage-list "A10-PyTorch-1, xxx" (OPTIONAL) : Only run the specified test stages. Examples: "A10-PyTorch-1, xxx". Note: Does NOT update GitHub check status.

--gpu-type "A30, H100_PCIe" (OPTIONAL) : Only run the test stages on the specified GPU types. Examples: "A30, H100_PCIe". Note: Does NOT update GitHub check status.

--test-backend "pytorch, cpp" (OPTIONAL) : Skip test stages which don't match the specified backends. Only support [pytorch, cpp, tensorrt, triton]. Examples: "pytorch, cpp" (does not run test stages with tensorrt or triton backend). Note: Does NOT update GitHub pipeline status.

--only-multi-gpu-test (OPTIONAL) : Only run the multi-GPU tests. Note: Does NOT update GitHub check status.

--disable-multi-gpu-test (OPTIONAL) : Disable the multi-GPU tests. Note: Does NOT update GitHub check status.

--add-multi-gpu-test (OPTIONAL) : Force run the multi-GPU tests in addition to running L0 pre-merge pipeline.

--post-merge (OPTIONAL) : Run the L0 post-merge pipeline instead of the ordinary L0 pre-merge pipeline.

--extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx" (OPTIONAL) : Run the ordinary L0 pre-merge pipeline and specified test stages. Examples: --extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx".

--detailed-log (OPTIONAL) : Enable flushing out all logs to the Jenkins console. This will significantly increase the log volume and may slow down the job.

--debug (OPTIONAL) : Experimental feature. Enable access to the CI container for debugging purpose. Note: Specify exactly one stage in the stage-list parameter to access the appropriate container environment. Note: Does NOT update GitHub check status.

For guidance on mapping tests to stage names, see docs/source/reference/ci-overview.md
and the scripts/test_to_stage_mapping.py helper.

kill

kill

Kill all running builds associated with pull request.

skip

skip --comment COMMENT

Skip testing for latest commit on pull request. --comment "Reason for skipping build/test" is required. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break.

reuse-pipeline

reuse-pipeline

Reuse a previous pipeline to validate current commit. This action will also kill all currently running builds associated with the pull request. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break.

Summary by CodeRabbit

  • New Features

    • Exposed LlmRequest.remove_lora_tensors to Python.
    • Added LoRA config-only support and auto-loading of weights from provided paths.
    • Centralized LoRA handling in the Python resource manager with caching, shape normalization, and GPU reuse.
    • Introduced dtype helper to convert binding dtypes to strings.
  • Refactor

    • Streamlined LoRA request processing and cache checks; simplified error propagation.
  • Tests

    • Reworked and expanded multi-LoRA coverage, including GPU/CPU cache scenarios and NeMo/CodeLlama cases, with appropriate GPU memory gating.

…IDIA#6510)

Signed-off-by: Amit Zuker <203509407+amitz-nv@users.noreply.github.com>
@amitz-nv amitz-nv requested review from a team as code owners August 11, 2025 08:31
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 11, 2025

📝 Walkthrough

Walkthrough

Adds LlmRequest::removeLoraTensors and exposes it to Python. Adjusts C++ LoRA cache checks and LoraConfig validation to allow config-only. Integrates LoRA handling in Python: propagates py_lora_path, passes lora_config to PeftCacheManager, centralizes per-request LoRA processing (load from checkpoint if needed, shape fixes, cache checks, tensor removal). Updates tests accordingly and adds dtype conversion utility.

Changes

Cohort / File(s) Change summary
LlmRequest API + bindings
cpp/include/tensorrt_llm/batch_manager/llmRequest.h, cpp/tensorrt_llm/batch_manager/llmRequest.cpp, cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp, cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp
Add removeLoraTensors() declaration/implementation and expose as remove_lora_tensors in nanobind/pybind.
PEFT cache manager (C++)
cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp
Simplify determineNumPages: direct taskId usage, remove custom PeftTaskNotCachedException path, rethrow runtime_error when no config, unchanged behavior when no taskId.
LoRA config validation (C++)
cpp/tensorrt_llm/executor/loraConfig.cpp
Split constructor checks: allow config-only; require config when weights provided; validate weights shape/device and cross-validate dims; updated messages.
Python LoRA request propagation
tensorrt_llm/executor/worker.py, tensorrt_llm/_torch/pyexecutor/llm_request.py, tensorrt_llm/_torch/pyexecutor/_util.py
Carry py_lora_path from worker to requests; include lora_config in executor creation; add py_lora_path attribute to LlmRequest and propagate from executor_request.
Python PEFT cache manager + dtype utils
tensorrt_llm/_torch/pyexecutor/resource_manager.py, tensorrt_llm/_utils.py
Extend PeftCacheManager(init, ensure_batch): accept lora_config, build LoraModelConfig, manage LoraManager, centralize add_request_peft (cache check, optional load from checkpoint, unsqueeze, call impl, remove tensors when cached); add binding_to_str_dtype and reverse dtype map.
Tests: LoRA API and cache scenarios
tests/unittest/llmapi/test_llm.py, tests/unittest/llmapi/test_llm_pytorch.py
Replace one parametrized multi-LoRA test with helper and multiple targeted tests; add NeMo/CodeLlama/GQA LoRA tests; add GPU memory guards.
Tests: Torch resource manager
tests/unittest/_torch/test_resource_manager.py
Update to construct richer LlmRequest, pass LoraConfig to PeftCacheManager, remove extra dim expansions; unskip a test.
Tests: LoraConfig rules
cpp/tests/unit_tests/executor/loraConfigTest.cpp
Update invalidInputs: allow config-only; disallow weights-only with new error text.
Test utilities cleanup
tests/unittest/utils/util.py
Remove multiprocessing/env helpers; streamline imports and keep remaining utilities.

Sequence Diagram(s)

sequenceDiagram
  participant Worker
  participant PeftCacheManager as Py PeftCacheManager
  participant LoraManager
  participant CppImpl as C++ Impl
  participant Req as LlmRequest

  Worker->>PeftCacheManager: add_request_peft(Req)
  alt Req.lora_task_id is cached
    PeftCacheManager->>Req: remove_lora_tensors()
  else not cached
    alt Req.lora_weights missing and py_lora_path present
      PeftCacheManager->>LoraManager: load_from_ckpt(py_lora_path, LoraModelConfig, source)
      LoraManager-->>PeftCacheManager: weights
      PeftCacheManager->>Req: set lora_weights
    end
    PeftCacheManager->>Req: ensure shapes (unsqueeze)
  end
  PeftCacheManager->>CppImpl: add_request_peft(Req, True)
  CppImpl-->>PeftCacheManager: task layer/module configs
  PeftCacheManager-->>Worker: updated Req
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • omera-nv
  • venkywonka
  • shaharmor98
  • Superjomn
  • FrankD412
  • nv-guomingz
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (7)
tensorrt_llm/executor/worker.py (1)

375-386: Fix TOCTOU race around LoRA CPU cache check; gate weights only for PyTorch backend

There’s a known race when using is_adapter_in_cpu_cache() to decide whether to omit weights (see our past learning). You compute adapter_in_cache before attempting a load, then unconditionally call _load_lora_adapter(). Re-evaluating cache status after the load reduces the window. Also, only omit weights for the PyTorch backend where a CPU peft cache is effectively shared; for TRT backend keep sending weights.

Proposed adjustment:

-            adapter_in_cache = self._lora_manager.is_adapter_in_cpu_cache(
-                request.lora_request.adapter_id)
-            self._load_lora_adapter(request.lora_request)
+            # Load the adapter from checkpoint if missing; no-op if already loaded.
+            self._load_lora_adapter(request.lora_request)
+            # Check cache status after the load to minimize TOCTOU race.
+            adapter_in_cache = self._lora_manager.is_adapter_in_cpu_cache(
+                request.lora_request.adapter_id)
             uid = str(request.lora_request.adapter_id)
-            lora_config = tllm.LoraConfig(
-                task_id=request.lora_request.adapter_id,
-                weights=self._lora_manager.cpp_lora_weights[uid]
-                if not adapter_in_cache else None,
-                config=self._lora_manager.cpp_lora_config[uid])
+            # For TRT backend, always send weights. For PyTorch backend, omit weights if present in CPU cache.
+            send_weights = (not self._is_pytorch_backend) or (not adapter_in_cache)
+            lora_config = tllm.LoraConfig(
+                task_id=request.lora_request.adapter_id,
+                weights=self._lora_manager.cpp_lora_weights[uid] if send_weights else None,
+                config=self._lora_manager.cpp_lora_config[uid])
             py_lora_path = request.lora_request.lora_path

This keeps TRT safe and narrows the race for PyTorch.

tensorrt_llm/_torch/pyexecutor/_util.py (1)

503-509: PeftCacheManager signature validated; lora_config propagation is correct.

Nit: duplicate ModelConfig imports in tensorrt_llm/_torch/pyexecutor/_util.py—remove or alias one to avoid ambiguity.

  • Line 10: from tensorrt_llm._torch.model_config import ModelConfig
  • Line 21: from ..model_config import ModelConfig
cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (1)

97-97: Build breaker: py::classh should be py::class_.

These occurrences will fail to compile. Replace with py::class_.

-    py::classh<GenLlmReq>(m, "GenericLlmRequest")
+    py::class_<GenLlmReq>(m, "GenericLlmRequest")
...
-    py::classh<tb::LlmRequest, GenLlmReq>(m, "LlmRequest", pybind11::dynamic_attr())
+    py::class_<tb::LlmRequest, GenLlmReq>(m, "LlmRequest", pybind11::dynamic_attr())

Also applies to: 258-258

cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)

117-117: Test bug: writing to the wrong buffer (weightsData instead of configData).

This invalidates the serialization test for config.

-            weightsData[i * configDim1 + j] = 3 * (i * configDim1 + j);
+            configData[i * configDim1 + j] = 3 * (i * configDim1 + j);
cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1)

338-342: Prefer using LlmRequest::removeLoraTensors to clear LoRA data.

Simplify and centralize cleanup with the new API.

-            // free memory associated with lora weights in llmRequest
-            req->clearLoraWeights();
-            req->clearLoraConfig();
+            // free memory associated with LoRA tensors in the request
+            req->removeLoraTensors();
tests/unittest/_torch/test_resource_manager.py (1)

244-249: Enforce LoRA config dtype to int32 when converting from NumPy

The executor expects LoRA config to be INT32. Make the dtype explicit to avoid accidental int64 loads on some platforms.

-        if lora_config is not None:
-            lora_config = torch.from_numpy(lora_config)
+        if lora_config is not None:
+            lora_config = torch.from_numpy(lora_config).to(torch.int32)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (1)

1237-1242: Fix ensure_batch return annotation

Tests treat ensure_batch’s return as a mapping (request_id -> peft table). The annotation says List[LlmRequest], which is misleading.

-    def ensure_batch(self,
+    def ensure_batch(self,
                      context_batch: List[LlmRequest],
                      generation_batch: List[LlmRequest],
-                     reset_gpu_cache: bool = False) -> List[LlmRequest]:
+                     reset_gpu_cache: bool = False) -> dict:
         return self.impl.ensure_batch(context_batch, generation_batch,
                                       reset_gpu_cache)

If there’s a concrete type for the peft table entries, prefer annotating that mapping precisely.

🧹 Nitpick comments (11)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)

288-289: Carry py_lora_path in LlmRequest: LGTM; please document the new field

The addition is consistent with other py_* attributes and child-copying behavior. Please add a short docstring/comment in the constructor documenting py_lora_path semantics (expected to be an on-disk LoRA ckpt path, optional), per guidelines.

tensorrt_llm/_utils.py (1)

198-202: Type annotate and document binding_to_str_dtype

Minor polishing for clarity and Sphinx docs.

-def binding_to_str_dtype(binding_dtype) -> str:
-    ret = _binding_to_str_dtype.get(binding_dtype)
+def binding_to_str_dtype(binding_dtype: DataType) -> str:
+    """Return the string dtype name corresponding to a bindings DataType."""
+    ret = _binding_to_str_dtype.get(binding_dtype)
     assert ret is not None, f'Unsupported binding dtype: {binding_dtype}'
     return ret
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)

2351-2353: Prefer Doxygen-style comment for new public API

Use Doxygen single-line comments for headers to match project guidelines.

-    // Remove LoRA weights and LoRA config tensors
-    void removeLoraTensors();
+    //! Remove LoRA weights and LoRA config tensors.
+    void removeLoraTensors();
tensorrt_llm/_torch/pyexecutor/_util.py (1)

8-13: Duplicate ModelConfig imports.

ModelConfig is imported twice from different paths; keep a single authoritative import to avoid ambiguity.

-from tensorrt_llm._torch.model_config import ModelConfig
-...
-from ..model_config import ModelConfig
+# Prefer one consistent import; for example:
+from tensorrt_llm._torch.model_config import ModelConfig

Also applies to: 21-24

cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)

88-90: Misleading comment (optional).

Comment says "Wrong memory type" but this case checks mismatched shapes. Update for clarity.

-        // Wrong memory type
+        // Shapes not matching
cpp/tensorrt_llm/executor/loraConfig.cpp (1)

41-56: Tighten weights branch usability: small refactor and message polish

  • Consider binding value() once to make intent and safety clearer and avoid repeated value() calls.
  • Error messages mix “lora”/“LoRA”. Prefer consistent “LoRA” for clarity.
  • Optional: if there’s a required dtype for weights (e.g., FP16/FP32), validate it here; otherwise, please confirm weights dtype is intentionally unconstrained.

Apply this minimal refactor for readability and consistency:

-    if (mWeights.has_value())
+    if (mWeights.has_value())
     {
-        SizeType32 constexpr expectedWeightsDims = 2;
-        TLLM_CHECK_WITH_INFO(
-            mConfig.has_value(), "Request for LoRA inference with lora weights must also have lora config");
+        SizeType32 constexpr expectedWeightsDims = 2;
+        TLLM_CHECK_WITH_INFO(
+            mConfig.has_value(), "Request for LoRA inference with LoRA weights must also have LoRA config");
 
-        TLLM_CHECK_WITH_INFO(
-            mWeights.value().getShape().size() == expectedWeightsDims, "Expected weights tensor to have 2 dimensions");
+        auto const& config = mConfig.value();
+        auto const& weights = mWeights.value();
+        TLLM_CHECK_WITH_INFO(
+            weights.getShape().size() == expectedWeightsDims, "Expected weights tensor to have 2 dimensions");
 
-        TLLM_CHECK_WITH_INFO(mWeights.value().getMemoryType() != MemoryType::kGPU
-                && mWeights.value().getMemoryType() != MemoryType::kUNKNOWN,
-            "Expected lora weights to be in CPU memory");
+        TLLM_CHECK_WITH_INFO(weights.getMemoryType() != MemoryType::kGPU
+                && weights.getMemoryType() != MemoryType::kUNKNOWN,
+            "Expected LoRA weights to be in CPU memory");
 
-        TLLM_CHECK_WITH_INFO(mConfig.value().getShape()[0] == mWeights.value().getShape()[0],
-            "Expected dim 0 of lora weights and lora config to have the same size");
+        TLLM_CHECK_WITH_INFO(config.getShape()[0] == weights.getShape()[0],
+            "Expected dim 0 of LoRA weights and LoRA config to have the same size");
     }
tests/unittest/_torch/test_resource_manager.py (3)

12-12: Follow Python import namespace guideline

Per project guidelines: always maintain the namespace when importing in Python. Prefer importing the module, then referencing the class.

-from tensorrt_llm._torch.pyexecutor.llm_request import LlmRequest
+from tensorrt_llm._torch.pyexecutor import llm_request as py_llm_request

And below (Line 251), construct via:

-        request = LlmRequest(
+        request = py_llm_request.LlmRequest(

251-260: Consider adding py_lora_path coverage

You now support lazy loading via py_lora_path. To exercise that path, add a test variant that sets request.py_lora_path and omits lora_weights, ensuring add_request_peft loads from checkpoint as intended.

I can draft such a test if helpful.


265-269: Docstring nit: clarify expected shapes and dtypes

Add that weights are float16 and config is int32, both 2D, to make expectations explicit.

-        """Create mock LoRA weights and config.
+        """Create mock LoRA weights and config.
+
+        Weights: float16, 2D CPU tensor.
+        Config: int32, 2D CPU tensor.
tests/unittest/llmapi/test_llm.py (1)

1462-1480: Avoid duplicating the helper across test suites

This helper mirrors logic added in the PyTorch test file. Consider centralizing it in tests/unittest/llmapi/lora_test_utils.py and reusing from both places to reduce drift.

tests/unittest/llmapi/test_llm_pytorch.py (1)

314-333: Mirror helper exists in TRT tests—consider centralizing

This helper duplicates the variant in tests/unittest/llmapi/test_llm.py. To reduce drift, move it to llmapi/lora_test_utils.py and import from both test files.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 824feb8 and 6e67c55.

📒 Files selected for processing (16)
  • cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1 hunks)
  • cpp/tensorrt_llm/batch_manager/llmRequest.cpp (1 hunks)
  • cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1 hunks)
  • cpp/tensorrt_llm/executor/loraConfig.cpp (1 hunks)
  • cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp (1 hunks)
  • cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (1 hunks)
  • cpp/tests/unit_tests/executor/loraConfigTest.cpp (1 hunks)
  • tensorrt_llm/_torch/pyexecutor/_util.py (1 hunks)
  • tensorrt_llm/_torch/pyexecutor/llm_request.py (2 hunks)
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py (4 hunks)
  • tensorrt_llm/_utils.py (2 hunks)
  • tensorrt_llm/executor/worker.py (3 hunks)
  • tests/unittest/_torch/test_resource_manager.py (7 hunks)
  • tests/unittest/llmapi/test_llm.py (2 hunks)
  • tests/unittest/llmapi/test_llm_pytorch.py (4 hunks)
  • tests/unittest/utils/util.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{cpp,h,hpp,cc,cxx}

📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)

**/*.{cpp,h,hpp,cc,cxx}: Closing braces of namespaces should have a comment saying the namespace it closes (e.g., } // namespace foo).
Prefer const or constexpr variables over #defines whenever possible.
A variable that is not modified after its initialization should be declared as const.
Except 0 (used for checking signness/existence/emptiness), nullptr, true, false, all other literals should only be used for variable initialization.
Use the Allman indentation style for braces in C++ code.
Put the semicolon for an empty for or while loop in a new line.
The statement forming the body of a switch, while, do..while, or for statement shall be a compound statement (use brace-delimited statements).
If and else should always be followed by brace-delimited statements, even if empty or a single statement.
C++ filenames should use camel case with the first letter lowercase (e.g., thisIsAFilename.cpp), and all files involved in a compilation target must have case-insensitive unique filenames.
All types (including class names) should use camel case with uppercase first letter (e.g., FooBarClass).
Local variables, methods, and namespaces should use camel case with first letter lowercase (e.g., localFooBar).
Non-magic-number global variables that are non-static and not defined in anonymous namespace should use camel case prefixed by 'g' (e.g., gDontUseGlobalFoos).
Non-magic-number global variables that are static or defined in an anonymous namespace should use camel case prefixed by 's' (e.g., sMutableStaticGlobal).
Locally visible static variables should use camel case with lowercase prefix 's' as the first letter (e.g., static std::once_flag sFlag;).
Class member variables should use camel case prefixed with 'm' (e.g., mNbFooValues). Public member variables do not require the 'm' prefix but it is encouraged for clarity.
Enumerations, global constants, static constants at class-scope, and function-scope magic-number/literal constants should be uppercase snake case with prefix...

Files:

  • cpp/tensorrt_llm/batch_manager/llmRequest.cpp
  • cpp/include/tensorrt_llm/batch_manager/llmRequest.h
  • cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp
  • cpp/tests/unit_tests/executor/loraConfigTest.cpp
  • cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp
  • cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp
  • cpp/tensorrt_llm/executor/loraConfig.cpp
**/*.{cpp,h,hpp,cc,cxx,cu,py}

📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)

All TensorRT-LLM Open Source Software code should contain an NVIDIA copyright header that includes the current year. This includes .cpp, .h, .cu, .py, and any other source files which are compiled or interpreted.

Files:

  • cpp/tensorrt_llm/batch_manager/llmRequest.cpp
  • cpp/include/tensorrt_llm/batch_manager/llmRequest.h
  • tensorrt_llm/_utils.py
  • cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp
  • cpp/tests/unit_tests/executor/loraConfigTest.cpp
  • cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp
  • tensorrt_llm/_torch/pyexecutor/llm_request.py
  • cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp
  • tensorrt_llm/_torch/pyexecutor/_util.py
  • tests/unittest/utils/util.py
  • tests/unittest/_torch/test_resource_manager.py
  • cpp/tensorrt_llm/executor/loraConfig.cpp
  • tensorrt_llm/executor/worker.py
  • tests/unittest/llmapi/test_llm.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
  • tests/unittest/llmapi/test_llm_pytorch.py
**/*.{h,hpp}

📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)

Use a preprocessor guard in header files. The guard name must have prefix TRTLLM_ followed by the filename, all in caps, and no trailing underscore.

Files:

  • cpp/include/tensorrt_llm/batch_manager/llmRequest.h
**/*.py

📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)

**/*.py: Python code should conform to Python 3.8+.
Indent Python code with 4 spaces. Do not use tabs.
Always maintain the namespace when importing in Python, even if only one class or function from a module is used.
Python filenames should use snake_case (e.g., some_file.py).
Python classes should use PascalCase (e.g., class SomeClass).
Python functions and methods should use snake_case (e.g., def my_awesome_function():).
Python local variables should use snake_case. Prefix k for variable names that start with a number (e.g., k_99th_percentile).
Python global variables should use upper snake_case and prefix G (e.g., G_MY_GLOBAL).
Python constants should use upper snake_case (e.g., MY_CONSTANT).
Avoid shadowing variables declared in an outer scope in Python.
Initialize all externally visible members of a Python class in the constructor.
For interfaces that may be used outside a Python file, prefer docstrings over comments.
Comments in Python should be reserved for code within a function, or interfaces that are local to a file.
Use Google style docstrings for Python classes and functions, which can be parsed by Sphinx.
Attributes and variables in Python can be documented inline; attribute docstrings will be rendered under the class docstring.
Avoid using reflection in Python when functionality can be easily achieved without it.
When using try-except blocks in Python, limit the except to the smallest set of errors possible.
When using try-except blocks to handle multiple possible variable types in Python, keep the body of the try as small as possible, using the else block to implement the logic.

Files:

  • tensorrt_llm/_utils.py
  • tensorrt_llm/_torch/pyexecutor/llm_request.py
  • tensorrt_llm/_torch/pyexecutor/_util.py
  • tests/unittest/utils/util.py
  • tests/unittest/_torch/test_resource_manager.py
  • tensorrt_llm/executor/worker.py
  • tests/unittest/llmapi/test_llm.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
  • tests/unittest/llmapi/test_llm_pytorch.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: amitz-nv
PR: NVIDIA/TensorRT-LLM#5616
File: tensorrt_llm/executor/worker.py:375-384
Timestamp: 2025-07-17T09:01:27.402Z
Learning: In tensorrt_llm/executor/worker.py, the LoRA adapter cache optimization logic that checks `is_adapter_in_cpu_cache()` and conditionally passes None for weights/config has a known race condition issue that cannot be solved with simple error handling or verification checks. This is a known limitation that requires a more comprehensive solution.
📚 Learning: 2025-07-28T17:06:08.621Z
Learnt from: moraxu
PR: NVIDIA/TensorRT-LLM#6303
File: tests/integration/test_lists/qa/examples_test_list.txt:494-494
Timestamp: 2025-07-28T17:06:08.621Z
Learning: In TensorRT-LLM testing, it's common to have both CLI flow tests (test_cli_flow.py) and PyTorch API tests (test_llm_api_pytorch.py) for the same model. These serve different purposes: CLI flow tests validate the traditional command-line workflow, while PyTorch API tests validate the newer LLM API backend. Both are legitimate and should coexist.

Applied to files:

  • tests/unittest/_torch/test_resource_manager.py
📚 Learning: 2025-07-17T09:01:27.402Z
Learnt from: amitz-nv
PR: NVIDIA/TensorRT-LLM#5616
File: tensorrt_llm/executor/worker.py:375-384
Timestamp: 2025-07-17T09:01:27.402Z
Learning: In tensorrt_llm/executor/worker.py, the LoRA adapter cache optimization logic that checks `is_adapter_in_cpu_cache()` and conditionally passes None for weights/config has a known race condition issue that cannot be solved with simple error handling or verification checks. This is a known limitation that requires a more comprehensive solution.

Applied to files:

  • tensorrt_llm/executor/worker.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py
🧬 Code Graph Analysis (11)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
cpp/tensorrt_llm/batch_manager/llmRequest.cpp (2)
  • removeLoraTensors (368-372)
  • removeLoraTensors (368-368)
cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1)
cpp/tensorrt_llm/pybind/batch_manager/kvCacheManager.cpp (6)
  • llmRequest (169-172)
  • llmRequest (169-169)
  • llmRequest (245-248)
  • llmRequest (245-245)
  • llmRequest (277-280)
  • llmRequest (277-277)
cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)
cpp/tensorrt_llm/executor/loraConfig.cpp (1)
  • LoraConfig (25-57)
cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp (2)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
  • updatePerfMetrics (1807-1824)
cpp/tensorrt_llm/batch_manager/llmRequest.cpp (2)
  • removeLoraTensors (368-372)
  • removeLoraTensors (368-368)
cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (2)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
  • updatePerfMetrics (1807-1824)
cpp/tensorrt_llm/batch_manager/llmRequest.cpp (2)
  • removeLoraTensors (368-372)
  • removeLoraTensors (368-368)
tensorrt_llm/_torch/pyexecutor/_util.py (1)
tensorrt_llm/_torch/models/modeling_phi4mm.py (1)
  • lora_config (244-264)
tests/unittest/_torch/test_resource_manager.py (4)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)
  • LlmRequest (265-396)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (1)
  • PeftCacheManager (1170-1275)
tensorrt_llm/llmapi/llm_args.py (1)
  • PeftCacheConfig (791-857)
tensorrt_llm/lora_manager.py (1)
  • LoraConfig (236-252)
tensorrt_llm/executor/worker.py (2)
tensorrt_llm/lora_manager.py (1)
  • cpp_lora_config (1222-1223)
tensorrt_llm/_torch/models/modeling_phi4mm.py (1)
  • lora_request (267-288)
tests/unittest/llmapi/test_llm.py (1)
tests/unittest/llmapi/test_llm_pytorch.py (4)
  • test_llama_7b_multi_lora_evict_and_reload_lora_gpu_cache (336-345)
  • _check_llama_7b_multi_lora_evict_load_new_adapters (314-332)
  • test_llama_7b_multi_lora_evict_and_load_new_adapters_in_cpu_and_gpu_cache (349-358)
  • test_llama_7b_multi_lora_read_from_cache_after_insert (362-369)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (4)
tensorrt_llm/lora_manager.py (5)
  • LoraConfig (236-252)
  • LoraManager (682-1297)
  • LoraModelConfig (256-260)
  • lora_weights (1210-1211)
  • load_from_ckpt (785-817)
tensorrt_llm/_utils.py (2)
  • binding_to_str_dtype (198-201)
  • nvtx_range (842-861)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)
  • LlmRequest (265-396)
tensorrt_llm/executor/request.py (1)
  • ckpt_source (52-53)
tests/unittest/llmapi/test_llm_pytorch.py (1)
tests/unittest/llmapi/test_llm.py (4)
  • _check_llama_7b_multi_lora_evict_load_new_adapters (1461-1479)
  • test_llama_7b_multi_lora_evict_and_reload_lora_gpu_cache (1483-1492)
  • test_llama_7b_multi_lora_evict_and_load_new_adapters_in_cpu_and_gpu_cache (1496-1505)
  • test_llama_7b_multi_lora_read_from_cache_after_insert (1509-1516)
🔇 Additional comments (17)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)

494-495: Propagate py_lora_path from executor_request: LGTM

Safe getattr with default None. Matches the new LlmRequest field and the PyTorch backend flow.

tests/unittest/utils/util.py (1)

6-6: Typing import refinement: LGTM

Importing Any, Generator is correct and used below.

tensorrt_llm/executor/worker.py (1)

501-501: Attach py_lora_path to the backend request: LGTM

Forwarding the path enables on-demand adapter loading in the PyTorch backend.

cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp (1)

378-380: Nanobind exposure looks correct.

Binding names and targets match C++ methods; consistent with pybind naming.

cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (1)

384-386: Expose remove_lora_tensors via pybind — LGTM.

Naming and binding are consistent with nanobind and C++.

cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)

56-58: Updated validation semantics — looks correct.

Allowing config-only and disallowing weights-only with the clearer message aligns with implementation changes.

Also applies to: 60-62

cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1)

596-605: determineNumPages fallback safe — no callers expect a custom exception

  • All calls to determineNumPages(req) propagate std::runtime_error without catching a specialized exception:
    • cpp/tensorrt_llm/batch_manager/capacityScheduler.cpp (lines 260 & 306)
    • cpp/tensorrt_llm/runtime/loraCache.cpp (lines 187, 443 & 911)
    • Python/Nanobind bindings in kvCacheManager.cpp
    • Unit tests in cpp/tests/unit_tests/runtime/loraCacheTest.cpp
  • No occurrences of PeftTaskNotCachedException in the repository.
  • LlmRequest::removeLoraTensors() is defined, but there are no manual LORA–tensor resets elsewhere to replace.

No further changes required here.

cpp/tensorrt_llm/executor/loraConfig.cpp (1)

30-40: Config-only path validation looks correct; allowing config-only is a good change

You preserved CPU-memory and INT32 dtype checks and ensured 2D config; clean and consistent with downstream expectations. No functional issues found here.

tests/unittest/_torch/test_resource_manager.py (1)

277-282: Passing an empty LoraConfig() is acceptable here but can be brittle

PeftCacheManager builds a LoraModelConfig from lora_config. While you don't trigger load-from-ckpt in these tests, providing explicit lora_target_modules matching the mocked model can future-proof this test if py_lora_path coverage is added later.

Would you prefer I update these call sites to LoraConfig(lora_target_modules=['attn_q','attn_k','attn_v']) to align with the mock model?

Also applies to: 291-295, 325-329, 359-363

tests/unittest/llmapi/test_llm.py (1)

1483-1518: New LoRA eviction/reload tests: LGTM

Good coverage across GPU-only eviction/reload, CPU+GPU cache interplay, and cache read-after-insert. The @skip_gpu_memory_less_than_40gb gating is appropriate.

tensorrt_llm/_torch/pyexecutor/resource_manager.py (4)

1172-1176: Constructor signature updated: document and validate callsites

Adding lora_config is a good move. Please update the class docstring to document the new parameter and ensure all callsites are passing a LoraConfig (tests seem updated).


1205-1211: LoraModelConfig construction: LGTM

Using binding_to_str_dtype(model_config.data_type) keeps the dtype consistent with backend expectations.


1254-1267: Centralizing LoRA handling in prepare_resources: LGTM

Using add_request_peft for each context request and then propagating py_lora_task_layer_module_configs keeps the flow consistent.


1213-1235: LoRA cache race can’t be fixed with simple try/catch
The known TOCTOU in the LoRA adapter cache (tensorrt_llm/executor/worker.py around lines 375–384) cannot be addressed via catch-and-retry or minor tensor guards. This race requires a broader refactor of caching and eviction logic rather than the proposed lightweight wrapper. Please disregard the suggested diff and plan a comprehensive solution for cache consistency.

Likely an incorrect or invalid review comment.

tests/unittest/llmapi/test_llm_pytorch.py (3)

336-346: Expanded LoRA eviction/reload coverage: LGTM

Good scenarios (GPU-only, CPU+GPU interplay, and cache reuse after insert). The 40GB gating is appropriate for resource-heavy runs.

Also applies to: 349-359, 362-370


373-391: “Evict and reload previously evicted adapters” scenario: strong coverage

This directly validates reuse-after-CPU-eviction behavior. Thanks for encoding the expected sequence of cache operations in the docstring.


430-447: Override behavior test: LGTM

Confirms that LoraConfig cache sizes override peft_cache_config; properly gated and consistent with new behavior.

Copy link
Collaborator

@tomeras91 tomeras91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving on behalf of nemotron devs

Copy link
Collaborator

@shaharmor98 shaharmor98 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@amitz-nv amitz-nv removed request for 2ez4bz and byshiue August 11, 2025 10:44
Copy link
Collaborator

@Superjomn Superjomn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on the llmapi changes

@shaharmor98 shaharmor98 enabled auto-merge (squash) August 11, 2025 10:46
@shaharmor98
Copy link
Collaborator

/bot run

@tensorrt-cicd
Copy link
Collaborator

PR_Github #14799 [ run ] triggered by Bot

@amitz-nv amitz-nv self-assigned this Aug 11, 2025
@tensorrt-cicd
Copy link
Collaborator

PR_Github #14799 [ run ] completed with state SUCCESS
/LLM/release-1.0/L0_MergeRequest_PR pipeline #55 completed with status: 'SUCCESS'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants