-
Notifications
You must be signed in to change notification settings - Fork 1
Add component inspection API #378
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
Open
xiaoyu-work
wants to merge
16
commits into
main
Choose a base branch
from
xiaoyu/inspect-components-api
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
421ef8e
Add component inspection API
xiaoyu-work e136bca
Merge branch 'main' into xiaoyu/inspect-components-api
xiaoyu-work ce6eaa2
update model
xiaoyu-work eb47589
Align component inspection with HF runtime paths
xiaoyu-work ec5dbe5
Fix CUDA 12 package selection in GPU CI
xiaoyu-work f9a703c
Cover CTC component inspection routing
xiaoyu-work c27f53f
Merge branch 'main' into xiaoyu/inspect-components-api
xiaoyu-work b7062d0
Make ONNX parity temp files Windows-safe
xiaoyu-work e598afa
Merge main and resolve component inspection conflicts
xiaoyu-work 4fbfcc8
Align Qwen3-TTS component documentation
xiaoyu-work 91a4906
Address component inspection review feedback
xiaoyu-work 818b974
Resolve merge conflicts with main
Copilot 95a9f5c
Fix merge conflict: keep model_roles and __init__ in Gemma4Task
Copilot 665e50c
Release ORT sessions eagerly in OnnxModelSession.close
Copilot 1ebbda6
Skip Fun-ASR golden case in CI until HF model_type metadata is available
Copilot bb90ac0
Fix lint import order in ort inference test
Copilot 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,7 +59,9 @@ jobs: | |
| pip install -r requirements/ci/requirements.txt | ||
| pip install soundfile librosa ml_dtypes flatbuffers numpy packaging protobuf sympy coloredlogs | ||
| pip install -e '.[testing,transformers]' | ||
| pip install --pre --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/ort-cuda-12-nightly/pypi/simple/ onnxruntime-gpu onnxruntime-genai-cuda | ||
| # PyPI's ORT 1.27+ wheel requires CUDA 13, while these runners use CUDA 12. | ||
| pip install --pre --no-deps --index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/ onnxruntime-gpu | ||
|
Member
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. Thanks! |
||
| pip install --no-deps onnxruntime-genai-cuda | ||
|
|
||
| - name: Check for golden test data | ||
| id: check_golden | ||
|
|
||
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
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 |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| """Inspect a model's component layout without building it. | ||
|
|
||
| ``inspect_components`` reports the components mobius would produce for a model | ||
| (their package keys and optimization roles) **without** constructing graphs or | ||
| loading weights. External tools such as Olive use this to plan per-component | ||
| work — e.g. optimizing a VLM's ``decoder`` differently from its | ||
| ``vision_encoder`` — before calling :func:`mobius.build`. | ||
|
|
||
| The component names returned here are the same keys :func:`mobius.build` | ||
| produces in its :class:`~mobius._model_package.ModelPackage` (and therefore the | ||
| subfolder names ``ModelPackage.save`` writes for multi-component models). | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| __all__ = ["ComponentInfo", "inspect_components"] | ||
|
|
||
| import dataclasses | ||
| import logging | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
Comment on lines
+21
to
+24
|
||
|
|
||
|
|
||
| @dataclasses.dataclass(frozen=True) | ||
| class ComponentInfo: | ||
| """A single component of a model. | ||
|
|
||
| Attributes: | ||
| name: Component name. This is the ``ModelPackage`` key mobius produces | ||
| (and the subfolder name a multi-component export is saved under). | ||
| role: Optimization role of the component, e.g. ``"decoder"``, | ||
| ``"encoder"``, or ``"embedding"``. Mobius uses this to gate fusion | ||
| passes (only ``"decoder"`` receives GQA / QKV-packing). It is the | ||
| value declared in the task's ``model_roles``. | ||
| source_paths: Runtime ``named_modules()`` paths that make up this | ||
| component inside the full HuggingFace model. These are not | ||
| checkpoint/state-dict key prefixes. A single component may map to | ||
| multiple disjoint sub-modules (e.g. a decoder assembled from | ||
| ``model.layers``, ``model.norm`` and ``lm_head``), so this is a | ||
| tuple. Empty when the component is the whole model or the layout is | ||
| unknown. Tools such as Olive use these to optimize a submodule in | ||
| place before exporting the full model. | ||
| """ | ||
|
|
||
| name: str | ||
| role: str | ||
| source_paths: tuple[str, ...] = () | ||
|
|
||
|
|
||
| def _resolve_task_model_type_and_config( | ||
| model_id: str, task, trust_remote_code: bool | ||
| ) -> tuple[str, str | None, object | None]: | ||
| """Resolve the mobius task name for a model id without building it. | ||
|
|
||
| Mirrors the model_type/task resolution in :func:`mobius.build`, limited to | ||
| what is needed to pick a task and inspect class-level component metadata | ||
| (no module construction, no weight loading). | ||
| """ | ||
| import transformers | ||
|
|
||
| from mobius._config_resolver import _default_task_for_model, _try_load_config_json | ||
| from mobius._registry import _detect_fallback_registration, registry | ||
|
|
||
| try: | ||
| hf_config = transformers.AutoConfig.from_pretrained( | ||
| model_id, trust_remote_code=trust_remote_code | ||
| ) | ||
| except (ValueError, KeyError, OSError): | ||
| hf_config = _try_load_config_json(model_id) | ||
| if hf_config is None: | ||
| # An explicit task is enough to report component names and roles, | ||
| # but source paths require a model type and therefore stay empty. | ||
| if task is not None: | ||
| return task, None, None | ||
| raise ValueError( | ||
| f"Could not load a HuggingFace config for {model_id!r}. inspect_components supports " | ||
| "transformers/registry models; diffusers pipelines are not supported." | ||
| ) from None | ||
|
Comment on lines
+78
to
+81
|
||
|
|
||
| model_type = hf_config.model_type | ||
|
|
||
| # model_type adjustments that affect task selection (subset of build()): | ||
| # Qwen3.5-MoE ships the same model_type for text-only and VL checkpoints. | ||
| if model_type == "qwen3_5_moe" and getattr(hf_config, "vision_config", None) is not None: | ||
| model_type = "qwen3_5_moe_vl" | ||
| # wav2vec2/hubert/wavlm with a CTC head map to the mms (CTC) registration. | ||
| if model_type in ("wav2vec2", "hubert", "wavlm"): | ||
| architectures = getattr(hf_config, "architectures", None) or [] | ||
| if any("ForCTC" in arch for arch in architectures): | ||
| model_type = "mms" | ||
|
|
||
| if task is not None: | ||
| return task, model_type, hf_config | ||
| if model_type in registry: | ||
| return _default_task_for_model(model_type), model_type, hf_config | ||
|
|
||
| fallback = _detect_fallback_registration(hf_config) | ||
| if fallback is not None and fallback.task is not None: | ||
| return fallback.task, model_type, hf_config | ||
| return _default_task_for_model(model_type), model_type, hf_config | ||
|
|
||
|
|
||
| def _get_hf_component_sources( | ||
| module_class: type, | ||
| model_type: str, | ||
| hf_config: object, | ||
| ) -> dict[str, tuple[str, ...]]: | ||
| """Read runtime HuggingFace component paths from a registered model class.""" | ||
| resolver = getattr(module_class, "get_hf_component_sources", None) | ||
| if resolver is not None: | ||
| return resolver(model_type=model_type, hf_config=hf_config) | ||
| return getattr(module_class, "HF_COMPONENT_SOURCES", {}) | ||
|
|
||
|
|
||
| def inspect_components( | ||
| model_id: str, | ||
| task=None, | ||
| trust_remote_code: bool = False, | ||
| ) -> list[ComponentInfo]: | ||
| """Return the components mobius would produce for a model. | ||
|
|
||
| Args: | ||
| model_id: HuggingFace model id or local path. | ||
| task: Optional task name (e.g. ``"vision-language"``) or | ||
| :class:`~mobius.tasks.ModelTask` instance. When ``None``, the task | ||
| is auto-detected from the model type. | ||
| trust_remote_code: Whether to trust remote code when loading the | ||
| HuggingFace config. | ||
|
|
||
| Returns: | ||
| A list of :class:`ComponentInfo`. Single-component models (most LLMs) | ||
| return a single entry named ``"model"``; multi-component models (VLMs, | ||
| encoder-decoders, speech models) return one entry per component. | ||
|
|
||
| Raises: | ||
| ValueError: If a config cannot be resolved for ``model_id`` (e.g. a | ||
| diffusers pipeline, which is not supported). | ||
| """ | ||
| from mobius._registry import registry | ||
| from mobius.tasks import get_task | ||
|
|
||
| resolved_task, model_type, hf_config = _resolve_task_model_type_and_config( | ||
| model_id, task, trust_remote_code | ||
| ) | ||
| task_obj = get_task(resolved_task) | ||
| roles = task_obj.model_roles or {} | ||
|
|
||
|
Comment on lines
+145
to
+150
|
||
| # Runtime HF paths are owned by the registered model class. Most classes | ||
| # declare a fixed ``HF_COMPONENT_SOURCES`` mapping; classes shared by | ||
| # several HF layouts can resolve paths from the already-loaded config. | ||
| component_sources: dict[str, tuple[str, ...]] = {} | ||
| if model_type is not None and hf_config is not None and model_type in registry: | ||
| module_class = registry.get(model_type) | ||
| component_sources = _get_hf_component_sources(module_class, model_type, hf_config) | ||
|
|
||
| components = [ | ||
| ComponentInfo( | ||
| name=name, | ||
| role=role, | ||
| source_paths=tuple(component_sources.get(name, ())), | ||
| ) | ||
| for name, role in roles.items() | ||
| ] | ||
| logger.debug( | ||
| "inspect_components(%s): task=%s components=%s", | ||
| model_id, | ||
| resolved_task, | ||
| [c.name for c in components], | ||
| ) | ||
| return components | ||
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to uninstall the onnxruntime cpu first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onnxruntime cpi is skipped in line 77 for cuda case