diff --git a/.agents/review-guidelines/technical-blog.md b/.agents/review-guidelines/technical-blog.md new file mode 100644 index 00000000000..6951afb82eb --- /dev/null +++ b/.agents/review-guidelines/technical-blog.md @@ -0,0 +1,42 @@ +# Technical blog review guideline + +Use this rubric when a pull-request comment requests a **technical blog** or +**technical announcement** review. It applies to public-facing documentation +such as `docs/source/announcements/` and complements the repository's normal +code-review guidance. + +## Review scope + +Review the changed announcement and its landing-page card together. Do not +review unrelated source files unless they provide evidence for a claim in the +post. + +## Checks + +1. **Factual support** — Every technical claim, performance number, and + comparison must be supported by a cited public source, a clearly identified + reproducible measurement, or a qualified statement. Flag claims that + overstate what the cited source establishes. +2. **Citation integrity** — Check that cited papers, repositories, checkpoints, + and issue or PR links exist and match the surrounding claim. Publication + dates must not precede the cited source's availability. +3. **Technical precision** — Preserve meaningful distinctions: measured versus + inferred results, training versus serving behavior, throughput versus + latency, architecture versus implementation detail, and public facts versus + internal context. +4. **Figure provenance** — Images need an accurate alt text and a source or + provenance that makes their public use appropriate. Captions and nearby + text must not imply a result the figure does not show. +5. **Public-release suitability** — Do not expose private infrastructure, + unreleased products, confidential benchmark data, credentials, internal + URLs, or claims that cannot be independently supported by public material. +6. **Reader clarity** — Verify the title, date, author, summary, tags, and + announcement-card metadata agree. Prefer precise terminology over marketing + shorthand when the two could be confused. + +## Findings + +Raise only material findings. Each finding should identify the exact claim, +explain the public-facing risk, and propose a concrete correction. Do not +duplicate routine style, spelling, or formatting feedback already handled by +CodeRabbit. diff --git a/docs/source/_static/announcements.css b/docs/source/_static/announcements.css new file mode 100644 index 00000000000..c588c9e1961 --- /dev/null +++ b/docs/source/_static/announcements.css @@ -0,0 +1,142 @@ +/* Scoped announcement styles for the Sphinx RTD theme. */ + +#announcements > h1, +#announcements > p, +.announcement-section { + max-width: 860px; + margin-left: auto; + margin-right: auto; +} + +.announcement-section { + width: 100%; +} + +.announcement-toolbar { + border: 1px solid #d6d8dc; + border-radius: 4px; + margin: 1.5rem 0; + padding: 1rem; + background: #f8f9fb; +} + +.announcement-search-label { + display: block; + font-weight: 700; + margin-bottom: 0.35rem; +} + +.announcement-search { + box-sizing: border-box; + width: 100%; + padding: 0.55rem 0.65rem; + border: 1px solid #b8bdc6; + border-radius: 4px; + font-size: 1rem; +} + +.announcement-tags { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + margin-top: 0.75rem; +} + +.announcement-tag { + border: 1px solid #9aa1ad; + border-radius: 999px; + padding: 0.28rem 0.65rem; + background: #fff; + color: #2f343d; + cursor: pointer; + font-size: 0.86rem; +} + +.announcement-tag.is-active, +.announcement-tag:hover { + border-color: #76b900; + background: #76b900; + color: #111; +} + +.announcement-grid { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 1rem; + margin: 1rem 0 1.25rem; +} + +.announcement-card { + border-bottom: 1px solid #d6d8dc; + padding: 0 0 1rem; +} + +.announcement-card:last-child { + border-bottom: 0; +} + +.announcement-card h2 { + margin-top: 0.25rem; + font-size: 1.2rem; + line-height: 1.35; +} + +.announcement-card p { + margin-bottom: 0.75rem; +} + +.announcement-card-meta { + color: #6b7280; + font-size: 0.85rem; +} + +.announcement-card-tags { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; +} + +.announcement-card-tags span { + border: 1px solid #d6d8dc; + border-radius: 999px; + color: #4b5563; + font-size: 0.78rem; + padding: 0.15rem 0.45rem; +} + +.announcement-empty { + border-left: 4px solid #76b900; + padding-left: 0.75rem; +} + + +.announcement-pager { + align-items: center; + display: flex; + gap: 0.75rem; + justify-content: flex-end; + margin: 0 0 2rem; +} + +.announcement-page-button { + border: 1px solid #9aa1ad; + border-radius: 4px; + background: #fff; + color: #2f343d; + cursor: pointer; + padding: 0.35rem 0.7rem; +} + +.announcement-page-button:disabled { + cursor: not-allowed; + opacity: 0.45; +} + +.announcement-page-status { + color: #4b5563; + font-size: 0.9rem; +} + +.toctree-wrapper.compound:empty { + display: none; +} diff --git a/docs/source/_static/announcements.js b/docs/source/_static/announcements.js new file mode 100644 index 00000000000..aec4feefd36 --- /dev/null +++ b/docs/source/_static/announcements.js @@ -0,0 +1,88 @@ +document.addEventListener('DOMContentLoaded', () => { + const search = document.querySelector('#announcement-search'); + const cards = Array.from(document.querySelectorAll('.announcement-card')).sort((left, right) => { + return (right.dataset.date || '').localeCompare(left.dataset.date || ''); + }); + const tags = Array.from(document.querySelectorAll('.announcement-tag')); + const empty = document.querySelector('#announcement-empty'); + const pager = document.querySelector('#announcement-pager'); + const prev = document.querySelector('#announcement-prev'); + const next = document.querySelector('#announcement-next'); + const status = document.querySelector('#announcement-page-status'); + const pageSize = 5; + + cards.forEach((card) => card.parentNode.appendChild(card)); + let activeTag = 'all'; + let currentPage = 1; + + if (!search || cards.length === 0) { + return; + } + + const matchingCards = () => { + const query = search.value.trim().toLowerCase(); + return cards.filter((card) => { + const haystack = + [ card.dataset.title, card.dataset.summary, card.dataset.tags ].join(' ').toLowerCase(); + const tagMatch = + activeTag === 'all' || (card.dataset.tags || '').split(' ').includes(activeTag); + const searchMatch = !query || haystack.includes(query); + return tagMatch && searchMatch; + }); + }; + + const update = () => { + const matches = matchingCards(); + const pageCount = Math.max(1, Math.ceil(matches.length / pageSize)); + currentPage = Math.min(currentPage, pageCount); + const start = (currentPage - 1) * pageSize; + const pageCards = new Set(matches.slice(start, start + pageSize)); + + cards.forEach((card) => { card.hidden = !pageCards.has(card); }); + + if (empty) { + empty.hidden = matches.length !== 0; + } + + if (pager && prev && next && status) { + pager.hidden = matches.length <= pageSize; + prev.disabled = currentPage <= 1; + next.disabled = currentPage >= pageCount; + status.textContent = `Page ${currentPage} of ${pageCount}`; + } + }; + + tags.forEach((button) => { + button.addEventListener('click', () => { + activeTag = button.dataset.tag || 'all'; + currentPage = 1; + tags.forEach((tag) => { + const selected = tag === button; + tag.classList.toggle('is-active', selected); + tag.setAttribute('aria-pressed', selected ? 'true' : 'false'); + }); + update(); + }); + }); + + search.addEventListener('input', () => { + currentPage = 1; + update(); + }); + + if (prev) { + prev.addEventListener('click', () => { + currentPage -= 1; + update(); + }); + } + + if (next) { + next.addEventListener('click', () => { + currentPage += 1; + update(); + }); + } + + update(); +}); diff --git a/docs/source/announcements/dspark-vs-domino.rst b/docs/source/announcements/dspark-vs-domino.rst new file mode 100644 index 00000000000..184cb45342c --- /dev/null +++ b/docs/source/announcements/dspark-vs-domino.rst @@ -0,0 +1,93 @@ +:orphan: + +DSpark vs Domino: Same DFlash Backbone, Different Correction Heads +################################################################## + +:Author: Model Optimizer Team +:Date: July 13, 2026 +:Tags: speculative-decoding, dflash, dspark, domino, architecture + +DSpark (DeepSpec) and Domino both build on block-parallel DFlash draft generation but diverge in their token-level correction heads. DSpark's default head is a stateless first-order Markov transition; Domino's is a GRU that conditions on the draft prefix. Both must unroll sequentially at inference, so the tradeoff is per-step cost against how much prefix context the correction can use. During teacher-forced training, DSpark's Markov transition can also be parallelized over positions. +See the DSpark and Domino papers in :ref:`dspark-domino-references` for the original method descriptions. + +Highlights +********** + +* Both systems share the DFlash block-parallel backbone, so their parallel draft throughput starts from a similar foundation. +* In ModelOpt, DSpark defaults to ``markov_head_type="vanilla"``: stateless ``W1`` and ``W2`` embedding lookups with no hidden state to thread through. +* Domino uses ``nn.GRU`` and carries recurrent state across draft positions. +* Both correction heads are sequential at inference because ``x_{k-1}`` must be sampled before step ``k``. + +Shared Foundation: DFlash Block-Parallel Backbone +************************************************* + +Both systems use DFlash: a draft backbone that runs a single causal attention forward pass over all draft positions in parallel, producing per-position hidden states and base draft logits. This is the expensive step; the correction head adds token-level adjustment on top of those outputs. + +Where They Diverge: The Correction Head +*************************************** + +DSpark uses a first-order Markov transition. For each draft position ``k``: + +.. code-block:: text + + e_{k-1} = W1[x_{k-1}] + bias_k = W2 * e_{k-1} + p_k = softmax(U_k + bias_k) + x_k ~ p_k + +The correction at position ``k`` depends only on ``x_{k-1}``; no RNN hidden state threads across steps. The dominant work is a table lookup and projection rather than a recurrent rollout. + +Domino uses a GRU correction head. A recurrent hidden state accumulates information about the draft prefix and is concatenated at readout: + +.. code-block:: text + + gru_h_k = GRU(input_k, gru_h_{k-1}) + p_k = softmax(U_k + W * [h_k; gru_h_k]) + x_k ~ p_k + +These descriptions compare the underlying architectures. ModelOpt's Domino support is currently training-only, so it does not apply the correction head in serving. + +Correction Head Comparison +************************** + +.. list-table:: + :header-rows: 1 + + * - System + - Per-step compute + - State carried + * - DSpark ``markov_head_type="vanilla"`` + - ``W1[x_{k-1}]`` plus transition projection + - None + * - Domino GRU + - Full GRU cell over a high-dimensional input + - Recurrent hidden state + +Both heads must unroll left-to-right at inference. The practical distinction is qualitative: the vanilla Markov head uses only the prior sampled token, while the GRU carries a prefix-dependent recurrent state. + +Takeaways +********* + +#. DFlash draft generation is shared; the correction head is the main differentiator. +#. Both default correction heads are sequential at inference; their tradeoff is local transition structure versus prefix-dependent state. +#. ModelOpt exposes the DSpark variants through ``markov_head_type``: ``vanilla`` (the default), ``gated``, and ``rnn``. The ``rnn`` option is the closest analogue to Domino's GRU. +#. Architectural comparisons do not establish a universal quality or throughput ranking; evaluate the chosen head on the target model and serving configuration. + +.. _dspark-domino-references: + +References +********** + +* Xin Cheng et al., `DSpark: Confidence-Scheduled Speculative Decoding with Semi-Autoregressive Generation `_, + arXiv:2607.05147, 2026. +* Jianuo Huang et al., `Domino: Decoupling Causal Modeling from Autoregressive Drafting in Speculative Decoding `_, + arXiv:2605.29707, 2026. + +Resources +********* + +* `DeepSpec / DSpark repo `_ +* `DeepSeek-V4-Pro-DSpark checkpoint `_ +* `Domino repo `_ +* `Domino checkpoint: Qwen3-8B-Domino-b16 `_ +* `ModelOpt PR #1710 `_ diff --git a/docs/source/announcements/github-pages-announcements.rst b/docs/source/announcements/github-pages-announcements.rst new file mode 100644 index 00000000000..62915e643fc --- /dev/null +++ b/docs/source/announcements/github-pages-announcements.rst @@ -0,0 +1,23 @@ +:orphan: + +Model Optimizer Announcements Are Moving to GitHub Pages +######################################################### + +:Author: Model Optimizer Team +:Date: August 13, 2026 +:Tags: release, docs, github-pages + +The Model Optimizer GitHub Pages site is expanding from API documentation into a lightweight announcement hub. The goal is to make releases, technical notes, examples, and deployment writeups easier to discover without introducing a separate publishing system. + +What Changes +************ + +* Announcements live in the documentation source and are reviewed through pull requests. +* The landing page defaults to announcements. +* Existing API documentation remains available from the Sphinx left navigation. +* Announcement pages support tags, search, filtering, and embedded images. + +Authoring Flow +************** + +Add a Sphinx page under ``docs/source/announcements/``, add its ``.announcement-card`` metadata and link to ``docs/source/index.rst``, and link it from the announcements toctree when applicable. The GitHub Pages workflow rebuilds the static site from committed source, so every announcement follows the same review path as code and docs. diff --git a/docs/source/conf.py b/docs/source/conf.py index e4b68ce7643..9f24b299fcb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -116,7 +116,8 @@ html_static_path = ["_static"] html_title = f"Model Optimizer {version}" -html_css_files = ["custom.css"] +html_css_files = ["custom.css", "announcements.css"] +html_js_files = ["announcements.js"] html_permalinks_icon = "#" # default icon not rendering properly # TODO: left here as reference for future diff --git a/docs/source/index.rst b/docs/source/index.rst index 80b27a851df..1962cac4949 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,7 +1,59 @@ -Welcome to Model Optimizer (ModelOpt) documentation! -#################################################### +Announcements +############# + +Release notes, technical updates, examples, and deployment stories from the Model Optimizer team. + +.. raw:: html + +
+
+ + +
+ + + + + + + + + +
+
+ +
+ + +
+ + + +
.. toctree:: + :hidden: + :maxdepth: 1 + :caption: Announcements + + self + +.. toctree:: + :hidden: :glob: :maxdepth: 1 :caption: Getting Started @@ -18,6 +70,7 @@ Welcome to Model Optimizer (ModelOpt) documentation! Quick Start: Sparsity .. toctree:: + :hidden: :glob: :maxdepth: 1 :caption: Guides @@ -25,6 +78,7 @@ Welcome to Model Optimizer (ModelOpt) documentation! guides/[0-9]* .. toctree:: + :hidden: :glob: :maxdepth: 1 :caption: Deployment @@ -32,14 +86,15 @@ Welcome to Model Optimizer (ModelOpt) documentation! deployment/[0-9]* .. toctree:: + :hidden: :glob: :maxdepth: 1 :caption: Examples examples/[0-9]* - .. toctree:: + :hidden: :glob: :maxdepth: 1 :caption: Reference @@ -47,6 +102,7 @@ Welcome to Model Optimizer (ModelOpt) documentation! reference/[0-9]* .. toctree:: + :hidden: :glob: :maxdepth: 1 :caption: Support