2828import os
2929import re
3030import sys
31+ import traceback
3132from dataclasses import asdict , dataclass , field
3233from enum import Enum
3334from pathlib import Path
@@ -56,11 +57,21 @@ class RequestKind(Enum):
5657 EMBEDDING = 2
5758
5859
60+ @dataclass
61+ class TritonLoraConfig :
62+ name : str
63+
64+ # Unique fields for TensorRT-LLM backend
65+ task_id : Optional [int ] = None
66+ path : Optional [str ] = None
67+ is_registered : Optional [bool ] = False
68+
69+
5970def _create_vllm_generate_request (
6071 model ,
6172 prompt ,
6273 request : CreateChatCompletionRequest | CreateCompletionRequest ,
63- lora_name : str | None ,
74+ lora_config : TritonLoraConfig | None ,
6475 echo_tensor_name : str | None ,
6576 default_max_tokens : int ,
6677):
@@ -135,8 +146,8 @@ def _create_vllm_generate_request(
135146 request_logprobs = True
136147 inputs ["return_logprobs" ] = np .bool_ ([request_logprobs ])
137148
138- if lora_name is not None :
139- sampling_parameters ["lora_name" ] = lora_name
149+ if lora_config is not None :
150+ sampling_parameters ["lora_name" ] = lora_config . name
140151
141152 guided_json = _get_guided_json_from_tool (request )
142153 if guided_json is not None :
@@ -167,15 +178,10 @@ def _create_trtllm_generate_request(
167178 model ,
168179 prompt ,
169180 request : CreateChatCompletionRequest | CreateCompletionRequest ,
170- lora_name : str | None ,
181+ lora_config : TritonLoraConfig | None ,
171182 echo_tensor_name : str | None ,
172183 default_max_tokens : int ,
173184):
174- if lora_name is not None :
175- raise ClientError (
176- "LoRA selection is currently not supported for TRT-LLM backend"
177- )
178-
179185 inputs = {}
180186 inputs ["text_input" ] = [[prompt ]]
181187 inputs ["stream" ] = np .bool_ ([[request .stream ]])
@@ -221,6 +227,21 @@ def _create_trtllm_generate_request(
221227 inputs ["guided_decoding_guide_type" ] = [["json_schema" ]]
222228 inputs ["guided_decoding_guide" ] = [[guided_json ]]
223229
230+ if lora_config is not None :
231+ # To perform inference with a specific LoRA for the first time `lora_task_id` `lora_weights` and `lora_config` must all be given.
232+ # The LoRA will be cached, so that subsequent requests for the same task only require `lora_task_id`.
233+ inputs ["lora_task_id" ] = np .uint64 ([[lora_config .task_id ]])
234+ if not lora_config .is_registered :
235+ lora_weights_data = np .load (
236+ os .path .join (lora_config .path , "model.lora_weights.npy" )
237+ )
238+ lora_config_data = np .load (
239+ os .path .join (lora_config .path , "model.lora_config.npy" )
240+ )
241+ inputs ["lora_weights" ] = lora_weights_data
242+ inputs ["lora_config" ] = lora_config_data
243+ lora_config .is_registered = True
244+
224245 inputs ["return_num_input_tokens" ] = np .bool_ ([[True ]])
225246 inputs ["return_num_output_tokens" ] = np .bool_ ([[True ]])
226247 return model .create_request (inputs = inputs )
@@ -594,9 +615,9 @@ def _get_guided_json_from_tool(
594615 return None
595616
596617
597- def _get_vllm_lora_names (
598- model_repository : str | list [str ], model_name : str , model_version : int
599- ) -> None | List [str ]:
618+ def _parse_lora_configs (
619+ model_repository : str | list [str ], model_name : str , model_version : int , backend : str
620+ ) -> None | List [tuple [ str , str ] ]:
600621 if (
601622 len (model_name ) == 0
602623 or model_name .isspace ()
@@ -606,7 +627,9 @@ def _get_vllm_lora_names(
606627 raise ValueError (
607628 f"Invalid model name: '{ model_name } '. Model names must be valid file-system-path segment names."
608629 )
609- lora_names = []
630+
631+ lora_configs = []
632+ lora_task_id = 1
610633 repo_paths = model_repository
611634 if isinstance (repo_paths , str ):
612635 repo_paths = [repo_paths ]
@@ -618,6 +641,7 @@ def _get_vllm_lora_names(
618641 raise ValueError (
619642 f"Invalid model name: '{ model_name } '. Model names must be valid file-system-path segment names."
620643 )
644+
621645 model_path = os .path .normpath (model_path )
622646 if not os .path .isdir (model_path ):
623647 # Cloud path?
@@ -632,26 +656,60 @@ def _get_vllm_lora_names(
632656 # Model directory is malformed?
633657 return None
634658 version_path = os .path .join (model_path , str (model_version ))
635- is_lora_enabled = False
636- model_file_path = os .path .join (version_path , "model.json" )
637- try :
638- with open (model_file_path , "r" ) as f :
639- config = json .load (f )
640- if "enable_lora" in config :
641- # The value could be a string or a bool.
642- is_lora_enabled = str (config ["enable_lora" ]).lower () == "true"
643- except Exception :
644- # Model directory or model.json is malformed?
645- return None
646- if is_lora_enabled != True :
647- continue
648659 lora_config_path = os .path .join (version_path , "multi_lora.json" )
660+
661+ if backend == "vllm" :
662+ is_lora_enabled = False
663+ model_file_path = os .path .join (version_path , "model.json" )
664+ try :
665+ with open (model_file_path , "r" ) as f :
666+ config = json .load (f )
667+ if "enable_lora" in config :
668+ # The value could be a string or a bool.
669+ is_lora_enabled = str (config ["enable_lora" ]).lower () == "true"
670+ except Exception :
671+ # Model directory or model.json is malformed?
672+ return None
673+ if is_lora_enabled != True :
674+ continue
675+ else :
676+ # TRT-LLM backend does not use model.json
677+ if not os .path .exists (lora_config_path ):
678+ continue
679+
649680 try :
650681 with open (lora_config_path , "r" ) as f :
651682 lora_config = json .load (f )
652- for lora_name in lora_config .keys ():
653- lora_names .append (lora_name )
654- except Exception :
683+ for lora_name , lora_path in lora_config .items ():
684+ print (f"backend: { backend } " )
685+ if backend == "vllm" :
686+ lora_configs .append (TritonLoraConfig (name = lora_name ))
687+ else :
688+ lora_weights_path = os .path .join (
689+ lora_path , "model.lora_weights.npy"
690+ )
691+ lora_config_path = os .path .join (
692+ lora_path , "model.lora_config.npy"
693+ )
694+ if not os .path .exists (lora_weights_path ):
695+ raise ServerError (
696+ f"LoRA weights file not found for '{ lora_name } ' at path: { lora_weights_path } "
697+ )
698+ if not os .path .exists (lora_config_path ):
699+ raise ServerError (
700+ f"LoRA config file not found for '{ lora_name } ' at path: { lora_config_path } "
701+ )
702+
703+ lora_configs .append (
704+ TritonLoraConfig (
705+ name = lora_name , path = lora_path , task_id = lora_task_id
706+ )
707+ )
708+ lora_task_id += 1
709+ except ServerError as e :
710+ raise e
711+ except Exception as e :
655712 # LoRA is enabled but its list is not provided or malformed?
713+ print (traceback .format_exc ())
656714 return None
657- return lora_names
715+ return lora_configs
0 commit comments