Add multi build support - #2480
Conversation
Introduce a top-level �uilds section on RunConfig that lets users declare multiple independent execution units (pipelines x devices x components) in one workflow config. * Add BuildConfigPartial / BuildConfig and a merge_build_default helper in olive/engine/config.py. _default lives inside �uilds as a sentinel key whose partial fields are merged into every sibling build with full-replace semantics (lists are not deep-merged). * Add �uilds: dict[str, BuildConfig] to RunConfig with an �xpand_build_defaults before-validator that pops _default and merges it into siblings, plus a �alidate_builds_references after-validator that checks pipeline/host/target/evaluator string refs resolve to known entries. * Schema-only change: the engine runner does not yet act on �uilds. Existing workflows without �uilds keep their current behavior. * Add 8 unit tests in test/workflows/test_run_config_builds.py covering the merge, override, full-replace, missing-field, invalid-ref, absent-builds and empty-default cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Execute the �uilds schema added in Phase 1.
* Add CompositeModelHandler.select_components(names) that returns the
unwrapped child handler when one name is given and a sliced
CompositeModelHandler otherwise. Unknown names raise a clear error.
* Add ModelConfig.select_components(names) so the runner can slice a
composite input config without materializing the full handler.
* Add a builds-aware execution branch in olive/workflows/run/run.py. When
�uilds is non-empty, the runner: validates components against the
composite input model, then loops over builds. For each build it builds a
per-build engine config (host/target/evaluator/search_strategy overrides
resolved against systems/evaluators), a per-build pipeline subset from
passes in the order declared by pipeline, the per-build accelerator
spec, and calls engine.run with build.output_dir. Returns
dict[build_name -> WorkflowOutput]. The no-builds path is unchanged and
still returns a single WorkflowOutput.
* Tests:
- 7 new composite handler / ModelConfig select_components cases in
test/model/test_composite_model.py.
- 7 new runner smoke tests in test/workflows/test_run_builds.py with
mocked Engine.run covering: no-builds backward compat, multi-build
dispatch, pipeline-subset ordering, per-build output_dir, host/target
override, non-composite + components error, unknown component error.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…us HfModel Support the two component-discovery paths from the multi-component design: - Flow A Option 2 (two steps): load a Mobius export directory as a CompositeModel, using per-component subfolder names as component names. Adds discover_onnx_components() and directory auto-discovery in CompositeModelHandler and ModelConfig.get_components/select_components. - Flow B (optimize then export): resolve an HfModel's components by querying Mobius (olive/common/mobius_utils.inspect_components, lazy import). HfModel.get_components returns Mobius component names; select_components tags the chosen component's submodule path in model_attributes for PyTorch-stage per-component passes. No 'input' build dependency is used. Build component validation updated for both sources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…exports When mobius exports a multi-component model, log each component's name and its ONNX file path so the export layout is visible in the run log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mobius.inspect_components returns frozen ComponentInfo dataclasses, but Olive's coercion only handled its own ComponentInfo or a plain dict and crashed calling .get() on a mobius object. Broaden ComponentInfo.coerce (renamed from from_dict) to also accept duck-typed objects exposing name/kind/source_path. Add test/common/test_mobius_utils.py covering the object, dict, passthrough, and missing-mobius paths that the existing inspect_components mocks never exercised. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The autouse _stub_mobius_module fixture guarded on whether mobius was already in sys.modules, which is False when real mobius is installed but not yet imported at fixture setup. It then injected a non-package stub that shadowed real mobius, breaking test_write_genai_config_requires_real_mobius (imports mobius.integrations). This was masked in CI where mobius is absent and the test is skipped. Guard on _HAS_REAL_MOBIUS instead so the fixture is a true no-op when mobius is installed. Also drop the log-capture assertion from test_multi_component_returns_composite_handler (logging stays in the pass). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
builds.components could only target CompositeModel (directory) and HfModel (via mobius); a DiffusersModel input fell through to get_components()/select_components() returning None, so the design's per-component diffusion example failed validation. Add an optional components filter to DiffusersModelHandler (restricts get_exportable_components to a subset in canonical variant order) and resolve/select diffusion components in ModelConfig. select_components scopes the handler so each build's conversion emits just that component's ONNX, with later passes auto-mapping over the single-component composite. Variant detection only reads config files, so resolution stays cheap at validation time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two runnable Flow A recipes plus a README documenting the two-step flow (CLI Mobius export, then an Olive config whose builds optimize each exported component): SD3 (transformer + VAE encoder/decoder; Mobius skips the CLIP/T5 text encoders) and Qwen3-VL-2B-Instruct (decoder/vision_encoder/embedding, the exact names Mobius produces). Both validate against RunConfig. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Copilot resolve the merge conflicts in this pull request |
…i/base.py and passes/onnx/mobius_model_builder.py
Merge conflicts resolved in commit
|
Now that mobius exports text encoders (CLIP-L, CLIP-G, T5-XXL), update the inference script to use ONNX Runtime for all components instead of PyTorch text encoders and VAE. Only tokenizers and the scheduler (pure logic, no neural network) remain as Python dependencies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
lintrunner found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 86248e38-d706-4b53-875d-8eb4c7c69b49
|
Copilot resolve the merge conflicts in this pull request |
Co-authored-by: xiaoyu-work <85524621+xiaoyu-work@users.noreply.github.com>
Resolved in commit |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 29 out of 29 changed files in this pull request and generated 3 comments.
Suppressed comments (4)
olive/passes/pytorch/quant_utils.py:330
- The same subtree-saving problem affects decoder/embedding fallbacks. For example, selecting T5's
sharedembedding makeswrapper.modelannn.Embedding;finalizethen accesses.configand.save_pretrained, so the newly added shared-embedding test cannot complete. Wrap the root model and let the component-path filter select only the requested weights.
if component_role in {"decoder", "embedding"}:
component_model = get_attr(root_model, slice_path) if slice_path else root_model
config = getattr(component_model, "config", root_model.config)
return _GenericComponentWrapper(component_model, config), (f"{slice_path}." if slice_path else "")
olive/passes/pytorch/quant_utils.py:606
skip_patternscontains root-relativemodules_to_not_convertnames, whileiter_quant_targetssees names relative to a sliced component. Thus an exclusion such asvision.projdoes not match localproj, and the explicitly excluded weight is quantized. Apply the pattern after convertingfull_nametoroot_name(while retaining identity-based skips).
skip_patterns=skip_patterns,
extra_skip_modules=excluded_attn_inputs,
):
root_name = _root_module_name(full_name, name_prefix)
# When the slice spans more than the component (multi-path components slice
olive/cli/base.py:346
- Changing this
eliftoifmakes--testmode fall through to the normal model message. In test mode the model is actually written under<output_path>/model(lines 315-320), but this branch printsModel is saved at <output_path>, which is misleading in addition to the test-report message. Preserve the mutually exclusive branch.
if not workflow_output.has_output_model():
olive/workflows/run/builds.py:56
- The new format is only recognized through this helper;
RunConfigitself still has nobuildsfield. Existing schema/validation consumers (docs/source/dump_schema.pyandskills/olive/scripts/validate_config.py) continue to useRunConfig, so they omit or reject every valid multi-build configuration even though the runtime accepts it. Expose the multi-build schema through the public run-config model or update all public schema/validation entry points to use a common top-level parser/model.
def parse_run_config(
run_config: Union[str, Path, dict],
) -> Union[RunConfig, OrderedDict[str, RunConfig]]:
Full-team review: PR #2480 "Add multi build support"Ran a 5-reviewer pass (readability / correctness / adversarial / spec-deep / cross-module integration) against the current diff ( Critical
Major
Minor
Open questions (not verified, need author input)
Praise
Net: the workflow-level |
|
Copilot resolve the merge conflicts in this pull request |
…ntLinear) Co-authored-by: xiaoyu-work <85524621+xiaoyu-work@users.noreply.github.com>
# Conflicts: # olive/common/hf/wrapper.py # olive/model/handler/mixin/hf.py # olive/passes/pytorch/quant_utils.py Co-authored-by: xiaoyu-work <85524621+xiaoyu-work@users.noreply.github.com>
Resolved. Merged |
Review: no Critical, but 6 Major findingsMulti-model review pass over the full diff. Every claim below was verified against the diff before posting (a couple of reviewer-generated "criticals" about Major (worth fixing before merge)1. artifact_dir = output_dir.parent if output_dir.suffix and not output_dir.is_dir() else output_dir
2.
3. For a single selection: child = selected[0]
child.model_attributes = {**(self.model_attributes or {}), **(child.model_attributes or {})}
return child
4.
Secondary, same hunk: 5. In 6. New public config surface has no schema and no docs
Minor / worth noting
Nits
What's good
AI-assisted review. Findings were cross-checked against the diff, but please verify before acting — particularly #4, which would benefit from a test with an on-disk quant config whose category flags conflict with the current run. |
Re-review of the update — all 6 Major findings resolved ✅Re-ran the review against the updated diff and checked out the branch to verify empirically rather than by reading alone. Every Major from the previous pass is genuinely fixed, and several fixes are better than what I suggested. Verified fixed1. Writable-dir overlap heuristic — the 2. 3. 4. assert match_skip("blocks.1", qcfg.modules_to_not_convert)
assert not match_skip("blocks.10", qcfg.modules_to_not_convert)5. 6. Schema and docs — Verification run
Remaining, all non-blocking
Nits
Nice work on #4 in particular — the AI-assisted review. Fixes were verified by checking out the branch, running the listed test targets, and executing |
This pull request introduces a new, flexible "builds" workflow to the Olive engine, enabling multiple independent build pipelines within a single run configuration. It adds support for per-build defaults, validation, and selective execution on model components, primarily targeting composite models. The changes also include robust schema validation and improved modularity for configuring builds.
Key changes include:
Builds Workflow and Configuration:
Added a new
buildsfield toRunConfig, allowing users to define multiple named build pipelines, each with its own pipeline, component selection, and system/evaluator overrides. A special_defaultkey enables partial defaults to be merged into sibling builds. (olive/engine/config.py,olive/workflows/run/config.py,olive/workflows/run/run.py) [1] [2] [3]Introduced
BuildConfigPartialandBuildConfigschemas for partial and full build configurations, and amerge_build_defaultfunction for merging defaults into builds. (olive/engine/config.py)Validation and Reference Resolution:
RunConfigto ensure build defaults are correctly merged and that all build references (passes, systems, evaluators) resolve to known entries. (olive/workflows/run/config.py) [1] [2]Component Selection for Composite Models:
select_componentsmethods in bothModelConfigandCompositeModelHandlerto allow builds to operate on specific named components of a composite model, returning either a single component or a sub-composite as needed. (olive/model/config/model_config.py,olive/model/handler/composite.py) [1] [2]Workflow Execution Logic:
_run_buildsfunction whenbuildsare present, running each build as an independent workflow with its own engine, pipeline, and input model slice. Includes helper functions for validation, engine config construction, and reference resolution. (olive/workflows/run/run.py) [1] [2]These improvements make the Olive engine significantly more flexible and modular, supporting advanced workflows for multi-component and multi-pipeline builds