Skip to content

Commit c23498a

Browse files
mc-nvyinggeh
andauthored
Updating "triton_cli" in order to support TensorRT-LLM 1.1.0 (#124)
Co-authored-by: Yingge He <157551214+yinggeh@users.noreply.github.com>
1 parent 7ecdc4d commit c23498a

6 files changed

Lines changed: 151 additions & 31 deletions

File tree

src/triton_cli/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python3
2-
# Copyright 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# Copyright 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
#
44
# Redistribution and use in source and binary forms, with or without
55
# modification, are permitted provided that the following conditions
@@ -66,7 +66,7 @@
6666
# Public
6767
"gpt2": "hf:gpt2",
6868
"opt125m": "hf:facebook/opt-125m",
69-
"mistral-7b": "hf:mistralai/Mistral-7B-v0.1",
69+
"mistral-7b": "hf:mistralai/Mistral-7B-Instruct-v0.1",
7070
"falcon-7b": "hf:tiiuae/falcon-7b",
7171
}
7272

src/triton_cli/repository.py

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# Copyright 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# Redistribution and use in source and binary forms, with or without
44
# modification, are permitted provided that the following conditions
@@ -323,13 +323,23 @@ def __generate_vllm_model(self, huggingface_id: str):
323323

324324
def __generate_ngc_model(self, name: str, source: str):
325325
engines_path = ENGINE_DEST_PATH + "/" + source
326+
# Find the actual engine directory that contains config.json
327+
actual_engine_dir = self.__find_engine_directory(engines_path)
326328
parse_and_substitute(
327-
str(self.repo), name, engines_path, engines_path, "auto", dry_run=False
329+
str(self.repo),
330+
name,
331+
actual_engine_dir,
332+
actual_engine_dir,
333+
"auto",
334+
dry_run=False,
328335
)
329336

330337
def __generate_trtllm_model(self, name: str, huggingface_id: str):
331338
engines_path = ENGINE_DEST_PATH + "/" + name
332-
engines = [engine for engine in Path(engines_path).glob("*.engine")]
339+
# Search for engine files recursively since they might be in subdirectories
340+
engines = list(Path(engines_path).glob("*.engine")) + list(
341+
Path(engines_path).glob("*/*.engine")
342+
)
333343
if engines:
334344
logger.warning(
335345
f"Found existing engine(s) at {engines_path}, skipping build."
@@ -343,21 +353,64 @@ def __generate_trtllm_model(self, name: str, huggingface_id: str):
343353
p.start()
344354
p.join()
345355

356+
# Find the actual engine directory that contains config.json
357+
# When using workspace parameter, TRT-LLM creates a subdirectory
358+
actual_engine_dir = self.__find_engine_directory(engines_path)
359+
346360
# NOTE: In every case, the TRT LLM template should be filled in with values.
347361
# If the model exists, the CLI will raise an exception when creating the model repo.
348362
# If a user clears the model repo, they won't need to re-build the engines,
349363
# but they will still need to modify the TRT LLM template.
350364
parse_and_substitute(
351365
triton_model_dir=str(self.repo),
352366
bls_model_name=name,
353-
engine_dir=engines_path,
354-
token_dir=engines_path,
367+
engine_dir=actual_engine_dir,
368+
token_dir=actual_engine_dir,
355369
token_type="auto",
356370
dry_run=False,
357371
)
358372

373+
def __find_engine_directory(self, workspace_path: str) -> str:
374+
"""
375+
Find the actual engine directory that contains config.json.
376+
When using the workspace parameter, TRT-LLM creates a subdirectory structure.
377+
This method searches for config.json and returns its parent directory.
378+
"""
379+
workspace_path = Path(workspace_path)
380+
381+
# First check if config.json exists directly in the workspace path
382+
if (workspace_path / "config.json").exists():
383+
return str(workspace_path)
384+
385+
# Search for config.json in subdirectories (up to 2 levels deep)
386+
for config_file in workspace_path.glob("*/config.json"):
387+
logger.info(f"Found engine directory at {config_file.parent}")
388+
return str(config_file.parent)
389+
390+
for config_file in workspace_path.glob("*/*/config.json"):
391+
logger.info(f"Found engine directory at {config_file.parent}")
392+
return str(config_file.parent)
393+
394+
# If no config.json found, return the original path and let the error surface
395+
logger.warning(
396+
f"Could not find config.json in {workspace_path} or its subdirectories. "
397+
f"Returning original path."
398+
)
399+
return str(workspace_path)
400+
359401
def __build_trtllm_engine(self, huggingface_id: str, engines_path: Path):
360-
from tensorrt_llm import LLM, BuildConfig
402+
# Ensure engines_path is a Path object
403+
engines_path = Path(engines_path)
404+
405+
# Import from _tensorrt_engine to force TensorRT backend (not PyTorch)
406+
# The PyTorch backend doesn't support workspace parameter
407+
try:
408+
from tensorrt_llm._tensorrt_engine import LLM
409+
except ImportError:
410+
# Fallback to regular import for newer versions
411+
from tensorrt_llm import LLM
412+
413+
from tensorrt_llm import BuildConfig
361414

362415
# NOTE: Given config.json, can read from 'build_config' section and from_dict
363416
config = BuildConfig()
@@ -367,13 +420,31 @@ def __build_trtllm_engine(self, huggingface_id: str, engines_path: Path):
367420
# config.max_seq_len = 8192
368421
# config.max_batch_size = 256
369422

370-
engine = LLM(huggingface_id, build_config=config)
371-
# TODO: Investigate if LLM is internally saving a copy to a temp dir
372-
engine.save(str(engines_path))
423+
# Create the workspace directory if it doesn't exist
424+
# TensorRT-LLM will create a temp subdir inside this workspace
425+
engines_path.mkdir(parents=True, exist_ok=True)
426+
427+
# Build engine to target directory using workspace parameter (TensorRT backend only)
428+
engine = LLM(huggingface_id, build_config=config, workspace=str(engines_path))
429+
430+
# For newer API versions with save() method, call it to ensure engine is properly saved
431+
# In older versions, the workspace parameter should have already placed the engine there
432+
if hasattr(engine, "save") and callable(getattr(engine, "save")):
433+
try:
434+
engine.save(str(engines_path))
435+
logger.debug(
436+
f"Called save() method to ensure engine is at {engines_path}"
437+
)
438+
except Exception as e:
439+
# If save fails, workspace parameter should have already placed it correctly
440+
logger.debug(
441+
f"save() call failed (engine may already be in workspace): {e}"
442+
)
373443

374444
# The new trtllm(v0.17.0+) requires explicit calling shutdown to shutdown
375445
# the mpi blocking thread, or the engine process won't exit
376-
engine.shutdown()
446+
if hasattr(engine, "shutdown") and callable(getattr(engine, "shutdown")):
447+
engine.shutdown()
377448

378449
def __create_model_repository(
379450
self, name: str, version: int = 1, backend: str = None

src/triton_cli/server/server_utils.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python3
22

3-
# Copyright 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# Copyright 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -210,9 +210,11 @@ def _parse_world_size(self) -> int:
210210
engine(s).
211211
"""
212212
assert self._is_trtllm_model, "World size cannot be parsed from a model repository that does not contain a TRT LLM model."
213+
engine_path = self._get_engine_path(self._trtllm_model_config_path)
214+
engine_config_path = engine_path / "config.json"
215+
216+
# Try to read config.json if it exists, otherwise use defaults
213217
try:
214-
engine_path = self._get_engine_path(self._trtllm_model_config_path)
215-
engine_config_path = engine_path / "config.json"
216218
with open(engine_config_path) as json_data:
217219
data = json.load(json_data)
218220
# FIXME: Revert handling using 'build_config' as the key when gpt migrates to using unified builder
@@ -226,8 +228,13 @@ def _parse_world_size(self) -> int:
226228
tp = int(config.get("tensor_parallel", 1))
227229
pp = int(config.get("pipeline_parallel", 1))
228230
return tp * pp
229-
except OSError:
230-
raise Exception(f"Unable to open {engine_config_path}")
231+
except FileNotFoundError:
232+
# If config.json doesn't exist, use default world size
233+
# Default tensor_parallel=1, pipeline_parallel=1, so world_size=1
234+
logger.warning(
235+
f"{engine_config_path} not found, using default world_size=1"
236+
)
237+
return 1
231238

232239

233240
class VLLMUtils:

src/triton_cli/templates/trt_llm/tensorrt_llm/config.pbtxt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# Copyright 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# Redistribution and use in source and binary forms, with or without
44
# modification, are permitted provided that the following conditions
@@ -756,3 +756,9 @@ parameters: {
756756
string_value: "${guided_decoding_backend}"
757757
}
758758
}
759+
parameters: {
760+
key: "xgrammar_tokenizer_info_path"
761+
value: {
762+
string_value: "${xgrammar_tokenizer_info_path}"
763+
}
764+
}

src/triton_cli/templates/trt_llm/tensorrt_llm_bls/config.pbtxt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# Copyright 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# Redistribution and use in source and binary forms, with or without
44
# modification, are permitted provided that the following conditions
@@ -299,6 +299,18 @@ input [
299299
data_type: TYPE_STRING
300300
dims: [ 1 ]
301301
optional: true
302+
},
303+
{
304+
name: "return_num_input_tokens"
305+
data_type: TYPE_BOOL
306+
dims: [ 1 ]
307+
optional: true
308+
},
309+
{
310+
name: "return_num_output_tokens"
311+
data_type: TYPE_BOOL
312+
dims: [ 1 ]
313+
optional: true
302314
}
303315
]
304316
output [
@@ -351,6 +363,16 @@ output [
351363
name: "kv_cache_alloc_total_blocks"
352364
data_type: TYPE_INT32
353365
dims: [ 1 ]
366+
},
367+
{
368+
name: "num_input_tokens"
369+
data_type: TYPE_UINT32
370+
dims: [ 1 ]
371+
},
372+
{
373+
name: "num_output_tokens"
374+
data_type: TYPE_UINT32
375+
dims: [ 1 ]
354376
}
355377
]
356378

src/triton_cli/trt_llm/engine_config_parser.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# Copyright 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# Redistribution and use in source and binary forms, with or without
44
# modification, are permitted provided that the following conditions
@@ -35,22 +35,33 @@ def parse_and_substitute(
3535
triton_model_dir, bls_model_name, engine_dir, token_dir, token_type, dry_run
3636
):
3737
json_path = engine_dir + "/config.json"
38-
with open(json_path) as j:
39-
config_file = json.load(j)
38+
39+
# Try to read config.json if it exists, otherwise use defaults
40+
config_file = None
41+
try:
42+
with open(json_path) as j:
43+
config_file = json.load(j)
44+
except FileNotFoundError:
45+
print(f"Warning: {json_path} not found, using default configuration")
4046

4147
config_dict = {}
4248
# These fields will cause parsing issues when parsing model config if not
4349
# replaced, so replace with sensible defaults.
4450

45-
# FIXME: Revert handling using 'build_config' as the key when gpt migrates to using unified builder
46-
build_config_key = (
47-
"builder_config"
48-
if config_file.get("builder_config") is not None
49-
else "build_config"
50-
)
51-
config_dict["triton_max_batch_size"] = config_file[build_config_key][
52-
"max_batch_size"
53-
]
51+
# Get max_batch_size from config.json if available, otherwise use default
52+
if config_file:
53+
# FIXME: Revert handling using 'build_config' as the key when gpt migrates to using unified builder
54+
build_config_key = (
55+
"builder_config"
56+
if config_file.get("builder_config") is not None
57+
else "build_config"
58+
)
59+
config_dict["triton_max_batch_size"] = config_file[build_config_key][
60+
"max_batch_size"
61+
]
62+
else:
63+
# Default max_batch_size when config.json is not available
64+
config_dict["triton_max_batch_size"] = 256
5465

5566
config_dict["logits_datatype"] = "TYPE_FP32"
5667
config_dict["triton_backend"] = "tensorrtllm" # or python
@@ -66,6 +77,9 @@ def parse_and_substitute(
6677
config_dict["engine_dir"] = engine_dir
6778
config_dict["tokenizer_dir"] = token_dir
6879
config_dict["tokenizer_type"] = token_type
80+
# Disable guided decoding by default (xgrammar requires additional configuration)
81+
config_dict["guided_decoding_backend"] = ""
82+
config_dict["xgrammar_tokenizer_info_path"] = ""
6983

7084
config_dict["max_queue_delay_microseconds"] = 0
7185
# Default echo = False

0 commit comments

Comments
 (0)