feat(inspect): render composite pipeline structure (pipeline-led)#903
feat(inspect): render composite pipeline structure (pipeline-led)#903timenick wants to merge 8 commits into
Conversation
Composite models (encoder-decoder seq2seq, etc.) previously inspected as a single non-runnable component (e.g. a bart decoder shown as Task: text2text-generation with a dangling encoder_hidden_states input). Inspect now surfaces the composite structure: - Model Information: the Task row shows the pipeline tasks the model_type serves (e.g. 'summarization · table-question-answering [composite]'), with an Export row for the component -> export-task breakdown. - A dedicated 'Composite Pipeline' panel lists the components and how to build one. - JSON: 'task' stays the granular machine task (unchanged contract); additive 'pipeline_tasks' + 'composite' fields. Gated on the resolver's detected composite (TaskResolution.composite), so only auto-detected composites render the view -- CLIP inspected for feature-extraction and other single-component exports are unchanged. pipeline_tasks are derived from the live COMPOSITE_MODEL_REGISTRY (new loader.composite_pipeline_tasks), architecture-agnostic. New API: loader.composite_pipeline_tasks; inspect.CompositeInfo + resolve_composite_info.
DingmaomaoBJTU
left a comment
There was a problem hiding this comment.
Good overall architecture — the pipeline-led composite view is clearly scoped (gated on resolver detection, not registry presence alone) and the additive JSON design is clean. I found a few correctness issues worth fixing before merge.
Address review on #903: resolve_composite_info now returns None when the detected composite yields no pipeline tasks for the model_type (registry divergence), instead of building a CompositeInfo with empty pipeline_tasks that renders a broken '[composite]' Task row. Add a regression test pinning the guard.
DingmaomaoBJTU
left a comment
There was a problem hiding this comment.
Follow-up review after commit 90f24b8.
Resolved: Finding #1 (downstream): the pipeline_tasks=[] guard was added in resolve_composite_info — CompositeInfo with empty pipeline_tasks no longer reaches the formatter. Finding #5: the registry-divergence monkeypatch test was added.
Still open: Three issues remain — the falsy None/{} conflation in the same guard, the issubclass registry inconsistency that is the root cause of finding #2, and the formatter's own lack of a guard (finding #3). The side-effect import warning (#4) is also unaddressed.
…tighten guards - register_composite_model now requires a WinMLCompositeModel subclass, making the registry invariant explicit at its single write point; the (now redundant) issubclass filters in composite_pipeline_tasks and _composite_components_for_task are removed so all readers uniformly trust the registry (matching resolve_composite) - resolve_composite_info gates on `detected_components is None` (not falsy) so a genuine empty-breakdown composite is not conflated with the un-set case - output_table guards both composite branches on pipeline_tasks so a directly constructed CompositeInfo with empty pipeline_tasks cannot render a bare [composite] Task row - tests: registry invariant, empty-components case, formatter empty-pipeline guard
…accessor - Add _composite_registry(): the single registry-load trigger the three readers (resolve_composite / composite_pipeline_tasks / _composite_components_for_task) now share. It raises RuntimeError when COMPOSITE_MODEL_REGISTRY is empty, so a moved/renamed registration fails loudly instead of silently returning []/None and disabling the composite feature unnoticed. - Fix a mypy break the main merge surfaced: #896 re-tightened the _composite_components_for_task annotation to type[WinMLCompositeModel] while the prior commit had removed that import; re-add it under TYPE_CHECKING. - Test: the accessor raises loudly when the registry is empty.
Summary
winml inspectnow surfaces the composite pipeline structure for composite models (encoder-decoder seq2seq, etc.). Previously a composite was shown as its single exported component — e.g. a bart decoder rendered asTask: text2text-generationwith a danglingencoder_hidden_statesinput ("non-runnable half"), with no hint that the model serves higher-level pipelines.Before
After (pipeline-led)
plus a dedicated Composite Pipeline panel listing the components and how to build a specific pipeline.
Behavior
TaskResolution.composite), so only models whose resolved task bridges to a composite show the view. A CLIP inspected forfeature-extraction(a runnable single encoder) and all non-composites are unchanged.COMPOSITE_MODEL_REGISTRY(newloader.composite_pipeline_tasks) — no hardcoded model/task names. bart →summarization · table-question-answering, marian →translation, qwen3 →text-generation, etc.taskstays the granular machine task (text2text-generation);pipeline_tasks+compositeare additive.API
loader.composite_pipeline_tasks(model_type) -> list[str]inspect.CompositeInfo,inspect.resolve_composite_info(...)Notes
--task/--model-classyields no composite view (pinscomposite=None) — the pipeline-led view is intentionally scoped to auto-detection.blip(infersimage-text-to-text, registers underimage-to-text) is not reached by the seq2seq detection bridge today; out of scope here.