docs: add announcements landing page (#1971)

## Summary
- Add a JS/static announcements landing page for GitHub Pages
- Keep existing Sphinx docs available under the `api/` subpath
- Add PR-authored announcement posts with tags, search/filtering, image
support, and a DSpark vs Domino sample post

Jira: https://jirasw.nvidia.com/browse/OMNIML-5476

## Verification
- `python3 docs/build_site.py --output docs/build/html`
- Local preview checked at `http://127.0.0.1:8088/`

## Publishing approval
User explicitly approved publishing the `dspark-vs-domino` sample post
and copied image assets from `modelopt-site` to public GitHub in
`NVIDIA/Model-Optimizer`.

## Notes
- Full `uv run nox -s docs` was attempted locally, but dependency
setup/download did not complete in a reasonable time; CI should provide
the authoritative full docs build and PR Pages preview.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added an interactive announcements hub to the documentation homepage
with date sorting, tag filtering, search, pagination, and empty-state
messaging.
* Improved announcement presentation with consistent cards, metadata,
typography, and interactive controls.

* **Documentation**
* Added announcements covering the GitHub Pages announcement hub and a
DSpark versus Domino comparison.
* Added guidance for authoring, reviewing, and discovering future
announcements.
* Updated documentation navigation to feature announcements while
keeping other sections accessible.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Chenhan Yu <chenhany@nvidia.com>
This commit is contained in:
Chenhan D. Yu
2026-08-13 21:04:54 +00:00
committed by GitHub
parent 686da8d893
commit ddca53b4bf
7 changed files with 449 additions and 4 deletions
@@ -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.
+142
View File
@@ -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;
}
+88
View File
@@ -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();
});
@@ -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 <https://arxiv.org/abs/2607.05147>`_,
arXiv:2607.05147, 2026.
* Jianuo Huang et al., `Domino: Decoupling Causal Modeling from Autoregressive Drafting in Speculative Decoding <https://arxiv.org/abs/2605.29707>`_,
arXiv:2605.29707, 2026.
Resources
*********
* `DeepSpec / DSpark repo <https://github.com/deepseek-ai/DeepSpec>`_
* `DeepSeek-V4-Pro-DSpark checkpoint <https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro-DSpark>`_
* `Domino repo <https://github.com/jianuo-huang/Domino>`_
* `Domino checkpoint: Qwen3-8B-Domino-b16 <https://huggingface.co/Huang2020/Qwen3-8B-Domino-b16>`_
* `ModelOpt PR #1710 <https://github.com/NVIDIA/Model-Optimizer/pull/1710>`_
@@ -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.
+2 -1
View File
@@ -116,7 +116,8 @@ html_theme_options = {
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
+59 -3
View File
@@ -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
<section class="announcement-section" aria-label="Announcement index">
<div class="announcement-toolbar">
<label class="announcement-search-label" for="announcement-search">Search announcements</label>
<input id="announcement-search" class="announcement-search" type="search" placeholder="Search announcements" autocomplete="off" />
<div class="announcement-tags" aria-label="Announcement tags">
<button class="announcement-tag is-active" type="button" data-tag="all" aria-pressed="true">All</button>
<button class="announcement-tag" type="button" data-tag="release" aria-pressed="false">Release</button>
<button class="announcement-tag" type="button" data-tag="speculative-decoding" aria-pressed="false">Speculative decoding</button>
<button class="announcement-tag" type="button" data-tag="dflash" aria-pressed="false">DFlash</button>
<button class="announcement-tag" type="button" data-tag="dspark" aria-pressed="false">DSpark</button>
<button class="announcement-tag" type="button" data-tag="domino" aria-pressed="false">Domino</button>
<button class="announcement-tag" type="button" data-tag="architecture" aria-pressed="false">Architecture</button>
<button class="announcement-tag" type="button" data-tag="docs" aria-pressed="false">Docs</button>
<button class="announcement-tag" type="button" data-tag="github-pages" aria-pressed="false">GitHub Pages</button>
</div>
</div>
<div class="announcement-grid" id="announcement-grid">
<article class="announcement-card" data-date="2026-08-13" data-title="Model Optimizer announcements are moving to GitHub Pages" data-summary="The public Model Optimizer site is gaining a PR-updated announcements landing page within the existing Sphinx documentation." data-tags="release docs github-pages">
<div class="announcement-card-meta">August 13, 2026 &middot; Model Optimizer Team</div>
<h2><a href="announcements/github-pages-announcements.html">Model Optimizer announcements are moving to GitHub Pages</a></h2>
<p>The GitHub Pages site now starts with announcements while the existing API documentation remains available in the docs navigation.</p>
<div class="announcement-card-tags"><span>release</span><span>docs</span><span>github-pages</span></div>
</article>
<article class="announcement-card" data-date="2026-07-13" data-title="DSpark vs Domino: Same DFlash Backbone, Different Correction Heads" data-summary="DSpark and Domino both build on block-parallel DFlash draft generation but diverge in their token-level correction heads." data-tags="speculative-decoding dflash dspark domino architecture">
<div class="announcement-card-meta">July 13, 2026 &middot; Model Optimizer Team</div>
<h2><a href="announcements/dspark-vs-domino.html">DSpark vs Domino: Same DFlash Backbone, Different Correction Heads</a></h2>
<p>DSpark and Domino share a DFlash backbone but make different correction-head tradeoffs: ModelOpt's <code>vanilla</code> Markov head versus a GRU.</p>
<div class="announcement-card-tags"><span>speculative-decoding</span><span>dflash</span><span>dspark</span><span>domino</span><span>architecture</span></div>
</article>
</div>
<p class="announcement-empty" id="announcement-empty" hidden>No announcements match this search.</p>
<nav class="announcement-pager" id="announcement-pager" aria-label="Announcement pages" hidden>
<button class="announcement-page-button" id="announcement-prev" type="button">Previous</button>
<span class="announcement-page-status" id="announcement-page-status"></span>
<button class="announcement-page-button" id="announcement-next" type="button">Next</button>
</nav>
</section>
.. 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 <https://github.com/NVIDIA/Model-Optimizer/tree/main/examples/llm_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