-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: Support explicit model control mode in OpenAI frontend #8682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
yinggeh
merged 33 commits into
main
from
spolisetty/tri-738-business-standard-ai-enterprise-czech-national-bank-add
Mar 24, 2026
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
66f2acc
Update
pskiran1 e56b855
Update
pskiran1 3fadc99
Update
pskiran1 10a77ef
Update python/openai/openai_frontend/main.py
pskiran1 7c20f6e
Update
pskiran1 e37e094
Merge branch 'spolisetty/tri-738-business-standard-ai-enterprise-czec…
pskiran1 3415d92
Update
pskiran1 3fa4695
Update
pskiran1 ff1e9be
Merge branch 'main' into spolisetty/tri-738-business-standard-ai-ente…
pskiran1 630cf14
Merge branch 'main' into spolisetty/tri-738-business-standard-ai-ente…
pskiran1 bb6538a
Update
pskiran1 c37b639
Fix pre-commit errors
pskiran1 21d3c71
Merge branch 'main' into spolisetty/tri-738-business-standard-ai-ente…
pskiran1 b9e85ce
Update
pskiran1 fed45f1
Merge branch 'spolisetty/tri-738-business-standard-ai-enterprise-czec…
pskiran1 ded522a
Update
pskiran1 0e68c9d
Update
pskiran1 ec73a43
Update
pskiran1 074bd46
Update
pskiran1 dc70f3c
Update
pskiran1 4f908b6
Merge branch 'main' into spolisetty/tri-738-business-standard-ai-ente…
pskiran1 647f74e
Update
pskiran1 70a6455
Merge branch 'spolisetty/tri-738-business-standard-ai-enterprise-czec…
pskiran1 509c09a
Merge branch 'main' into spolisetty/tri-738-business-standard-ai-ente…
pskiran1 4bfb5b9
Remove model name validation from the frontend, as the Core handles it.
pskiran1 88a9658
Fix pre-commit
pskiran1 f9a8cf6
Update
pskiran1 dca17fa
Fix trtllm generate_engine.py
yinggeh c55aee0
Merge branch 'spolisetty/tri-738-business-standard-ai-enterprise-czec…
yinggeh 02eb203
asfas
yinggeh 55af90c
Update tests
yinggeh ea5273f
Update docs
yinggeh 9db96f1
update test
yinggeh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import asyncio | ||
| import base64 | ||
| import json | ||
| import time | ||
|
|
@@ -137,10 +138,8 @@ def __init__( | |
| self.lora_separator = lora_separator | ||
| self.default_max_tokens = default_max_tokens | ||
|
|
||
| # NOTE: Creation time and model metadata will be static at startup for | ||
| # now, and won't account for dynamically loading/unloading models. | ||
| self.create_time = int(time.time()) | ||
| self.model_metadata = self._get_model_metadata() | ||
| self._metadata_lock = asyncio.Lock() | ||
| self.tool_call_parser = ( | ||
| ToolParserManager.get_tool_parser_cls(tool_call_parser) | ||
| if tool_call_parser | ||
|
|
@@ -493,53 +492,114 @@ def _get_tokenizer(self, tokenizer_name: str): | |
|
|
||
| return tokenizer | ||
|
|
||
| def _build_model_metadata(self, name: str) -> TritonModelMetadata: | ||
| model = self.server.model(name) | ||
| backend = model.config()["backend"] | ||
| if not backend and model.config()["platform"] == "ensemble": | ||
| backend = "ensemble" | ||
| print(f"Found model: {name=}, {backend=}") | ||
|
|
||
| lora_configs = _parse_lora_configs( | ||
| self.server.options.model_repository, | ||
| name, | ||
| model.version, | ||
| backend if self.backend is None else self.backend, | ||
| ) | ||
|
|
||
| echo_tensor_name = None | ||
| for input in model.config()["input"]: | ||
| if input["name"] in [ | ||
| "exclude_input_in_output", | ||
| "sampling_param_exclude_input_from_output", | ||
| ]: | ||
| echo_tensor_name = input["name"] | ||
| break | ||
|
|
||
| return TritonModelMetadata( | ||
| name=name, | ||
| backend=backend, | ||
| model=model, | ||
| tokenizer=self.tokenizer, | ||
| lora_configs=lora_configs, | ||
| echo_tensor_name=echo_tensor_name, | ||
| create_time=int(time.time()), | ||
| inference_request_converter=self._determine_request_converter( | ||
| backend, RequestKind.GENERATION | ||
| ), | ||
| embedding_request_converter=self._determine_request_converter( | ||
| backend, RequestKind.EMBEDDING | ||
| ), | ||
| ) | ||
|
|
||
| def _get_model_metadata(self) -> Dict[str, TritonModelMetadata]: | ||
| # One tokenizer and creation time shared for all loaded models for now. | ||
| # One tokenizer is shared for all loaded models; creation time is per model. | ||
| model_metadata = {} | ||
| for name, _ in self.server.models(exclude_not_ready=True).keys(): | ||
| model_metadata[name] = self._build_model_metadata(name) | ||
| return model_metadata | ||
|
|
||
| # Read all triton models and store the necessary metadata for each | ||
| for name, _ in self.server.models().keys(): | ||
| model = self.server.model(name) | ||
| backend = model.config()["backend"] | ||
| # Explicitly handle ensembles to avoid any runtime validation errors | ||
| if not backend and model.config()["platform"] == "ensemble": | ||
| backend = "ensemble" | ||
| print(f"Found model: {name=}, {backend=}") | ||
|
|
||
| lora_configs = _parse_lora_configs( | ||
| self.server.options.model_repository, | ||
| name, | ||
| model.version, | ||
| backend if self.backend is None else self.backend, | ||
| async def load_model(self, model_name: str) -> Model: | ||
| if ( | ||
| self.server.options.model_control_mode | ||
| != tritonserver.ModelControlMode.EXPLICIT | ||
| ): | ||
| raise ClientError( | ||
| "Model load/unload requires --model-control-mode=explicit" | ||
| ) | ||
|
|
||
| echo_tensor_name = None | ||
| for input in model.config()["input"]: | ||
| if input["name"] in [ | ||
| "exclude_input_in_output", | ||
| "sampling_param_exclude_input_from_output", | ||
| ]: | ||
| echo_tensor_name = input["name"] | ||
| break | ||
|
|
||
| metadata = TritonModelMetadata( | ||
| name=name, | ||
| backend=backend, | ||
| model=model, | ||
| tokenizer=self.tokenizer, | ||
| lora_configs=lora_configs, | ||
| echo_tensor_name=echo_tensor_name, | ||
| create_time=self.create_time, | ||
| inference_request_converter=self._determine_request_converter( | ||
| backend, RequestKind.GENERATION | ||
| ), | ||
| embedding_request_converter=self._determine_request_converter( | ||
| backend, RequestKind.EMBEDDING | ||
| ), | ||
| async with self._metadata_lock: | ||
| if model_name in self.model_metadata: | ||
| raise ClientError(f"Model '{model_name}' is already loaded") | ||
|
|
||
| # Blocking C API call dispatched to thread pool to avoid blocking | ||
| # the event loop. The C API blocks until model is fully loaded and | ||
| # ready, matching standard Triton server behavior. | ||
| try: | ||
| metadata = await asyncio.to_thread(self._load_model_sync, model_name) | ||
| except tritonserver.InvalidArgumentError as e: | ||
| raise ClientError(f"Failed to load model '{model_name}': {e}") | ||
| except tritonserver.TritonError as e: | ||
| raise ServerError(f"Failed to load model '{model_name}': {e}") | ||
|
|
||
| self.model_metadata[model_name] = metadata | ||
|
|
||
| return Model( | ||
| id=model_name, | ||
| created=metadata.create_time, | ||
| object=ObjectType.model, | ||
| owned_by="Triton Inference Server", | ||
| ) | ||
|
|
||
| def _load_model_sync(self, model_name: str) -> TritonModelMetadata: | ||
| self.server.load(model_name) | ||
| return self._build_model_metadata(model_name) | ||
|
|
||
| async def unload_model(self, model_name: str) -> None: | ||
| if ( | ||
| self.server.options.model_control_mode | ||
| != tritonserver.ModelControlMode.EXPLICIT | ||
| ): | ||
| raise ClientError( | ||
| "Model load/unload requires --model-control-mode=explicit" | ||
| ) | ||
| model_metadata[name] = metadata | ||
|
|
||
| return model_metadata | ||
| async with self._metadata_lock: | ||
| if model_name not in self.model_metadata: | ||
| raise ClientError(f"Unknown model: {model_name}") | ||
|
|
||
| # Blocking C API call dispatched to thread pool. The C API handles | ||
| # in-flight request draining and conflict resolution internally. | ||
| try: | ||
| await asyncio.to_thread(self._unload_model_sync, model_name) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. an await under lock? how long are we expecting the lock to be held here?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Depending on how long core loads/unloads the model |
||
| except tritonserver.InvalidArgumentError as e: | ||
| raise ClientError(f"Failed to unload model '{model_name}': {e}") | ||
| except tritonserver.TritonError as e: | ||
| raise ServerError(f"Failed to unload model '{model_name}': {e}") | ||
|
|
||
| del self.model_metadata[model_name] | ||
|
|
||
| def _unload_model_sync(self, model_name: str) -> None: | ||
| self.server.unload(model_name) | ||
|
|
||
| def _get_streaming_chat_response_chunk( | ||
| self, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.