Conversation
Signed-off-by: Lawrence Lane <llane@nvidia.com>
Signed-off-by: Lawrence Lane <llane@nvidia.com>
📝 WalkthroughWalkthroughThis PR migrates NeMo RL documentation from Sphinx/MyST to Fern-based MDX format, adding Fern infrastructure, migration/processing scripts, and comprehensive documentation covering algorithms, installation, design, guides, and configuration across 80+ new documentation files, along with submodule pointer updates. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| else: | ||
| if "/>" not in line[match.start() :]: | ||
| tag_stack.append(tag_name) |
There was a problem hiding this comment.
Self-closing detection is incorrect when multiple tags appear on the same line.
"/>" not in line[match.start():] scans the rest of the line from the current match, so a /> belonging to a later tag on the same line will prevent the current opening tag from being pushed onto the stack. For example, on <Tab> <Card />, the <Tab> match would see /> from <Card /> and be incorrectly treated as self-closing.
A more precise check would look for /> only within the current tag's boundaries:
Proposed fix
else:
- if "/>" not in line[match.start() :]:
+ # Check if THIS tag is self-closing by looking for /> before the next < or end of line
+ rest = line[match.end():]
+ next_open = rest.find("<")
+ segment = rest[:next_open] if next_open != -1 else rest
+ if "/>" not in segment:
tag_stack.append(tag_name)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/scripts/find_tag_mismatches.py` around lines 45 - 47, The current
self-closing detection uses '"/>" not in line[match.start():]' which can see a
later tag's '/>' on the same line; instead, locate the end of the current tag
and inspect only that tag's end. From the current regex match (use
match.start()), find tag_end = line.find('>', match.start()); if tag_end == -1
treat as non-self-closing (or skip), otherwise consider it self-closing only if
line[tag_end-1] == '/'; only call tag_stack.append(tag_name) when that per-tag
check shows it is not self-closing. Use the existing variables match, tag_name,
tag_stack to implement this.
| from pathlib import Path | ||
|
|
||
|
|
||
| def update_links_in_content(content: str, file_dir: Path, pages_root: Path) -> str: |
There was a problem hiding this comment.
Unused file_dir and pages_root parameters — relative paths not resolved.
Both parameters are accepted but never used (confirmed by Ruff ARG001). The current implementation naively prepends / to relative paths, turning ../page into /../page — which is invalid. This appears to be the source of the /../ link patterns seen in the output MDX files (e.g., evaluation.mdx lines 33 and 62).
If relative path resolution isn't needed, remove the unused parameters. If it is, file_dir and pages_root should be used to compute correct absolute paths.
Also applies to: 23-24
🧰 Tools
🪛 Ruff (0.15.0)
[warning] 12-12: Unused function argument: file_dir
(ARG001)
[warning] 12-12: Unused function argument: pages_root
(ARG001)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/scripts/update_links.py` at line 12, The function
update_links_in_content currently accepts file_dir and pages_root but doesn't
use them, causing naive prepending of "/" and producing invalid "/../" links;
fix by using file_dir and pages_root to resolve relative links: for each
relative href/path found in update_links_in_content, resolve it against file_dir
(e.g., (file_dir / link).resolve()), compute the path relative to pages_root,
convert to a POSIX-style path, and then prefix with "/" for the final link; if
the project doesn't require resolving links, remove file_dir and pages_root from
update_links_in_content's signature and all its call sites to eliminate the
unused-parameter warning.
| text, url = match.group(1), match.group(2) | ||
| if url.startswith(("http://", "https://", "#", "mailto:")): | ||
| return match.group(0) | ||
| clean = url.replace(".md", "").replace(".mdx", "") |
There was a problem hiding this comment.
Bug: .replace(".md", "") before .replace(".mdx", "") corrupts .mdx links.
Because .md is a substring of .mdx, replacing .md first turns page.mdx into pagex, and the subsequent .replace(".mdx", "") has nothing left to match. The fix is to replace .mdx first, or better, use a suffix-aware replacement.
Proposed fix
- clean = url.replace(".md", "").replace(".mdx", "")
+ # Strip .mdx before .md to avoid partial replacement (.mdx -> x)
+ clean = url.replace(".mdx", "").replace(".md", "")Or, for a more precise approach that only strips the file extension:
- clean = url.replace(".md", "").replace(".mdx", "")
+ # Only strip .md/.mdx file extensions, not occurrences mid-string
+ anchor = ""
+ if "#" in url:
+ url, anchor = url.rsplit("#", 1)
+ anchor = "#" + anchor
+ for ext in (".mdx", ".md"):
+ if url.endswith(ext):
+ url = url[: -len(ext)]
+ break
+ clean = url + anchor🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/scripts/update_links.py` at line 19, The replacement order on the line
that sets clean (currently using url.replace(".md", "").replace(".mdx", ""))
corrupts .mdx filenames; change it to strip the longer suffix first or, better,
use a suffix-aware approach: remove ".mdx" before ".md" or use a
file-extension-aware method (e.g., os.path.splitext or a regex that matches only
trailing extensions) when computing clean from url so only the file extension is
stripped correctly (update the expression that assigns clean in
update_links.py).
| > [!NOTE] | ||
| > For GB200 systems with 4 GPUs per node, use `--gres=gpu:4` instead. |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Fern MDX callout syntax GitHub Flavored Markdown
💡 Result:
Fern’s docs don’t use GitHub’s “alert/callout” blockquote syntax (the > [!NOTE] style). Instead, Fern provides MDX components for callouts. (buildwithfern.com)
Fern (MDX) callouts
<Note>This adds a note in your documentation.</Note>
<Warning title="Example callout" icon="skull-crossbones">
This callout uses a title and a custom icon.
</Warning>Supported variants include: <Info>, <Warning>, <Success>, <Error>, <Note>, <Launch>, <Tip>, <Check>. (buildwithfern.com)
GitHub Flavored Markdown (on GitHub) “alerts” syntax (not Fern)
> [!NOTE]
> Highlights information that users should take into account, even when skimming.(Also: TIP, IMPORTANT, WARNING, CAUTION.) (github.com)
If you’re thinking “MDX + remark-gfm”
MDX supports GFM features via remark-gfm, but that’s about tables/task lists/etc.—it doesn’t imply Fern will render GitHub’s alert blocks as callouts. (mdxjs.com)
Citations:
- 1: https://buildwithfern.com/learn/docs/writing-content/components/callouts?utm_source=openai
- 2: https://buildwithfern.com/learn/docs/writing-content/components/callouts?utm_source=openai
- 3: https://github.com/orgs/community/discussions/16925?utm_source=openai
- 4: https://mdxjs.com/guides/gfm/?utm_source=openai
🏁 Script executed:
# Search for callout patterns in .mdx files to see what syntax is used
rg -t mdx "(> \[!|<(Note|Warning|Info|Tip)" --max-count 20Repository: NVIDIA-NeMo/RL
Length of output: 140
🏁 Script executed:
# Search for callout patterns in .mdx files
rg -t mdx '(<Note|<Warning|<Info|<Tip|> \[!)' --max-count 20Repository: NVIDIA-NeMo/RL
Length of output: 85
🏁 Script executed:
# Find .mdx files in the repository
fd '\.mdx$' | head -20
# Then search for callout patterns
fd '\.mdx$' -x grep -l '(<Note|<Warning|> \[!)' {} \; 2>/dev/null | head -10
# Also check the specific file mentioned in the review
cat -n fern/v0.5.0/pages/about/algorithms/dapo.mdx | sed -n '65,75p'Repository: NVIDIA-NeMo/RL
Length of output: 1193
🏁 Script executed:
# Search for GFM callout syntax (unsupported) vs component syntax (supported)
echo "=== Files with unsupported GFM callout syntax > [!NOTE/WARNING/etc] ==="
grep -r '> \[!' fern/v0.5.0/pages/ --include="*.mdx" | head -20
echo ""
echo "=== Files with supported component syntax <Note>/<Warning>/etc ==="
grep -r '<Note\|<Warning\|<Info\|<Tip' fern/v0.5.0/pages/ --include="*.mdx" | head -20Repository: NVIDIA-NeMo/RL
Length of output: 1317
Replace unsupported GitHub-Flavored Markdown callout syntax with Fern MDX components.
Fern does not support the > [!NOTE] callout syntax. Change to Fern's component-based syntax:
<Note>
For GB200 systems with 4 GPUs per node, use `--gres=gpu:4` instead.
</Note>
This pattern appears in multiple files across the documentation (dapo.mdx, dpo.mdx, grpo.mdx, on-policy-distillation.mdx, rm.mdx, sft.mdx, evaluation.mdx, installation.mdx, quick-start.mdx, cluster.mdx). Consider a global search-and-replace to update all instances to the correct <Note>, <Warning>, <Tip>, or <Info> component syntax.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/about/algorithms/dapo.mdx` around lines 70 - 71, Replace
the unsupported GitHub-Flavored Markdown callout block "> [!NOTE] ... " with
Fern MDX component syntax by wrapping the content in the <Note> component (e.g.,
change the callout in dapo.mdx to use <Note>For GB200 systems with 4 GPUs per
node, use `--gres=gpu:4` instead.</Note>); search the listed files (dapo.mdx,
dpo.mdx, grpo.mdx, on-policy-distillation.mdx, rm.mdx, sft.mdx, evaluation.mdx,
installation.mdx, quick-start.mdx, cluster.mdx) and replace callouts with the
appropriate Fern components (<Note>, <Warning>, <Tip>, <Info>) keeping the
original inner text unchanged.
| uv run python examples/run_rm.py cluster.gpus_per_node=8 | ||
| ``` | ||
|
|
||
| Refer to the [RM documentation](/../../guides/rm) for more information. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's explore the repository structure to understand context
echo "=== Repository structure (top level) ==="
ls -la | head -20
echo ""
echo "=== Exploring fern directory ==="
find fern -type f -name "*.mdx" | head -30Repository: NVIDIA-NeMo/RL
Length of output: 2731
🏁 Script executed:
# Check how many files use this /../../ pattern and what they link to
echo "=== Internal doc links using /../../ ==="
rg -n '/\.\./\.\./guides/' --glob '*.mdx' | head -20
echo ""
echo "=== All /../../ link targets ==="
rg -oP '\(/\.\./\.\./([^)]+)\)' --glob '*.mdx' | sed 's/.*(\///' | sed 's/)//' | sort -u | head -30Repository: NVIDIA-NeMo/RL
Length of output: 2139
🏁 Script executed:
# Let's check the specific file mentioned in the review
echo "=== Content of rm.mdx ==="
cat -n fern/v0.5.0/pages/about/algorithms/rm.mdxRepository: NVIDIA-NeMo/RL
Length of output: 1887
🏁 Script executed:
#!/bin/bash
# Check if there's a Fern configuration file that describes link handling
echo "=== Fern configuration files ==="
find . -name "fern.json" -o -name "fern.yaml" -o -name "fern.config.*" 2>/dev/null | head -20
echo ""
echo "=== Check directory structure for guides ==="
find fern -type d -name "guides" 2>/dev/null
echo ""
echo "=== Actual pages directory structure ==="
find fern/v0.5.0/pages -maxdepth 3 -type d | sortRepository: NVIDIA-NeMo/RL
Length of output: 359
🏁 Script executed:
#!/bin/bash
# Let's check what links actually exist in the codebase - look for regular navigation patterns
echo "=== Links using /guides/ pattern (absolute style) ==="
rg -n '\[([^\]]+)\]\(/guides/' --glob '*.mdx' | head -20
echo ""
echo "=== Links using \.\./ pattern (relative style) ==="
rg -n '\]\(\.\.' --glob '*.mdx' | wc -l
echo "Total files with relative-style links (\.\./):"
rg '\]\(\.\.' --glob '*.mdx' | cut -d':' -f1 | sort -u | wc -l
echo ""
echo "=== Check if there are guides accessible from root ==="
find fern -type f -path '*/guides/*' | head -20Repository: NVIDIA-NeMo/RL
Length of output: 958
🏁 Script executed:
#!/bin/bash
# Check Fern documentation or comments about navigation
echo "=== Checking fern directory for README or docs ==="
find fern -type f \( -name "README*" -o -name "*.md" \) | head -10
echo ""
echo "=== Check if there's a nav or structure configuration ==="
find fern -type f \( -name "*.json" -o -name "*.yaml" -o -name "*.yml" \) | head -20Repository: NVIDIA-NeMo/RL
Length of output: 265
🏁 Script executed:
#!/bin/bash
# Check the Fern configuration to understand link resolution
echo "=== fern.config.json ==="
cat fern/fern.config.json
echo ""
echo "=== fern/README.md (checking for documentation) ==="
cat fern/README.md
echo ""
echo "=== docs.yml ==="
cat fern/docs.yml
echo ""
echo "=== versions/v0.5.0.yml ==="
cat fern/versions/v0.5.0.ymlRepository: NVIDIA-NeMo/RL
Length of output: 9061
🏁 Script executed:
#!/bin/bash
# Let's check the update_links.py script to understand what it does
echo "=== Checking update_links.py script ==="
cat fern/scripts/update_links.py 2>/dev/null || echo "Script not found"
echo ""
echo "=== List all scripts available ==="
ls -la fern/scripts/ | grep -E "\.py$"Repository: NVIDIA-NeMo/RL
Length of output: 2970
🏁 Script executed:
#!/bin/bash
# Let's check how other pages link to guides to see if there's a pattern
echo "=== Check links in guides pages themselves ==="
grep -h '\[.*\](' fern/v0.5.0/pages/guides/*.mdx | grep -E '\(/|http' | head -20
echo ""
echo "=== Check links in about pages ==="
grep -h '\[.*\](' fern/v0.5.0/pages/about/*.mdx | grep -E '\(/|http' | head -20
echo ""
echo "=== Check what link format appears in index pages ==="
cat fern/v0.5.0/pages/about/algorithms/index.mdx | grep -E '\[.*\]\('Repository: NVIDIA-NeMo/RL
Length of output: 8438
🏁 Script executed:
#!/bin/bash
# Let's check what the actual source of the content was - check if there are old docs
echo "=== Check for old docs directory ==="
find . -type d -name "docs" -not -path "./.git/*" | head -5
echo ""
echo "=== Check git history to see what the original links looked like ==="
git log --oneline --all -- fern/v0.5.0/pages/about/algorithms/rm.mdx | head -10
echo ""
echo "=== Check the actual commit where rm.mdx was added ==="
git log -p --follow -- fern/v0.5.0/pages/about/algorithms/rm.mdx | head -100Repository: NVIDIA-NeMo/RL
Length of output: 267
🏁 Script executed:
#!/bin/bash
# Let's understand the Fern link resolution better by testing different patterns
echo "=== Verify the discrepancy: index uses /rm but rm.mdx uses /../../guides/rm ==="
echo ""
echo "Index page links:"
grep -E '\[.*\]\(/[a-z]' fern/v0.5.0/pages/about/algorithms/index.mdx | head -10
echo ""
echo "rm.mdx links to guides:"
grep -E '\]\(' fern/v0.5.0/pages/about/algorithms/rm.mdx
echo ""
echo "Other algorithm pages:"
for file in fern/v0.5.0/pages/about/algorithms/*.mdx; do
if [ "$(basename $file)" != "index.mdx" ]; then
echo "=== $(basename $file) ==="
grep -E '\]\(' "$file" | grep -E 'guides|documentation' | head -3
fi
doneRepository: NVIDIA-NeMo/RL
Length of output: 2464
🏁 Script executed:
#!/bin/bash
# Let's check the about pages to see their link patterns
echo "=== Links in about/overview.mdx ==="
grep -E '\]\(/' fern/v0.5.0/pages/about/overview.mdx | head -10
echo ""
echo "=== Links in about/backends.mdx ==="
grep -E '\]\(/' fern/v0.5.0/pages/about/backends.mdx | head -10
echo ""
echo "=== Check guides pages - what pattern do they use ==="
echo "GRPO guide links:"
grep -E '\]\(/' fern/v0.5.0/pages/guides/grpo.mdx | head -5
echo ""
echo "DPO guide links:"
grep -E '\]\(/' fern/v0.5.0/pages/guides/dpo.mdx | head -5Repository: NVIDIA-NeMo/RL
Length of output: 3395
🏁 Script executed:
#!/bin/bash
# The real question: does Fern support /../../ style links or should it be /guides/?
# Let's check if there's any Fern documentation in the repo
echo "=== Check Fern documentation in README ==="
grep -i "link\|path\|relative\|absolute" fern/README.md
echo ""
echo "=== Check if there's a problem with these links in Fern docs ==="
# Search the old docs for how links were structured
if [ -d "docs" ]; then
echo "Checking original docs structure:"
find docs -name "*.md" -type f | head -5
echo ""
echo "Sample link from old docs:"
grep -h '\[.*\](' docs/*.md 2>/dev/null | grep -E 'guides|algorithms' | head -3
fiRepository: NVIDIA-NeMo/RL
Length of output: 1694
🏁 Script executed:
#!/bin/bash
# Let's verify what the correct Fern convention actually is
echo "=== Check the original docs to see what the links were ==="
if [ -d "docs/about/algorithms" ]; then
echo "Original rm.md:"
cat docs/about/algorithms/rm.md | grep -E '\[.*\]\(' | head -10
fi
echo ""
echo "=== Summary: which pattern is actually used for page-to-page links ==="
echo "Pattern /guides/something (direct root-relative):"
rg -c '\(/guides/' fern/v0.5.0/pages --glob '*.mdx' | head -5
echo ""
echo "Pattern /../../guides/ (parent-relative):"
rg -c '\(/\.\./\.\./guides/' fern/v0.5.0/pages --glob '*.mdx' | head -5
echo ""
echo "Pattern /../ (single parent):"
rg -c '\(/\.\.' fern/v0.5.0/pages --glob '*.mdx' | wc -l
rg '\(/\.\.[^/]' fern/v0.5.0/pages --glob '*.mdx' | cut -d':' -f2 | sort -u | head -10Repository: NVIDIA-NeMo/RL
Length of output: 736
🏁 Script executed:
#!/bin/bash
# Final verification: confirm what the correct path should be
echo "=== Confirm: Fern pages directory structure for routing ==="
echo "Structure from navigation config:"
grep "path: " fern/versions/v0.5.0.yml | grep -E "(guides|algorithms)" | head -20
echo ""
echo "=== The actual problem: inconsistency in link patterns ==="
echo "Index page (algorithms/index.mdx) uses:"
grep '\]\(/' fern/v0.5.0/pages/about/algorithms/index.mdx | head -3
echo ""
echo "Algorithm pages (rm.mdx, dpo.mdx, etc) use:"
grep '\]\(/' fern/v0.5.0/pages/about/algorithms/rm.mdx | grep -E 'guides|documentation'
echo ""
echo "=== What the update_links.py would have produced ==="
echo "Original: ../../guides/rm.md → Expected output: /guides/rm"
echo "Actual output in files: /../../guides/rm"
echo ""
echo "This suggests the update_links.py script may not have been run,"
echo "or links were manually edited after conversion."Repository: NVIDIA-NeMo/RL
Length of output: 1726
🏁 Script executed:
#!/bin/bash
# Let me verify the correct format one more time with simpler patterns
echo "=== Check index page - what format does it use for guide links ==="
cat fern/v0.5.0/pages/about/algorithms/index.mdx | grep -A 1 "RM\|DPO\|GRPO\|SFT" | grep -E '\[.*\]' | head -10
echo ""
echo "=== Confirm the pattern used in about/overview.mdx ==="
cat fern/v0.5.0/pages/about/overview.mdx | grep '\]\(' | head -5
echo ""
echo "=== The conversion rule from update_links.py ==="
echo "Input: ../../guides/rm.md (file-relative)"
echo "Expected output: /guides/rm (Fern root-relative)"
echo "Actual in file: /../../guides/rm"
echo ""
echo "This is definitely wrong. /../../ would try to go up directory levels"
echo "from the Fern root, which doesn't make sense in a web routing context."Repository: NVIDIA-NeMo/RL
Length of output: 1145
Update internal doc links from /../../guides/ to /guides/ format.
This link pattern (/../../guides/rm) is incorrect for Fern's routing system. Fern uses root-relative paths (e.g., /guides/rm), not filesystem-relative paths with /../. The index page correctly demonstrates this with links like /grpo, /dpo, /rm, /sft.
This affects at least these files:
fern/v0.5.0/pages/about/algorithms/rm.mdx:24:/../../guides/rm→/guides/rmfern/v0.5.0/pages/about/algorithms/sft.mdx:6:/../../guides/sft→/guides/sftfern/v0.5.0/pages/about/algorithms/grpo.mdx:8:/../../guides/grpo→/guides/grpofern/v0.5.0/pages/about/algorithms/dpo.mdx:38:/../../guides/dpo→/guides/dpo
Also review guide pages for similar issues with /../../examples/ and /../../nemo_rl/ links.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/about/algorithms/rm.mdx` at line 24, Replace
filesystem-relative internal links that use "/../../guides/..." with
root-relative paths (e.g., change "/../../guides/rm" to "/guides/rm") in the
affected MDX files (notably the link in rm.mdx referencing "/../../guides/rm" as
well as sft.mdx, grpo.mdx, and dpo.mdx); update each anchor so it begins with
"/guides/..." and then scan guide pages for similar filesystem-relative patterns
like "/../../examples/" and "/../../nemo_rl/" and convert them to root-relative
"/examples/..." and "/nemo_rl/..." respectively to match Fern's routing
conventions.
|
|
||
| For our simple example above, this would look like: | ||
|
|
||
| ```{testcode} |
There was a problem hiding this comment.
Sphinx doctest syntax ({testcode}, {testoutput}) is not valid in Fern MDX.
The ```{testcode} and ```{testoutput} fences are Sphinx/MyST directives. Fern MDX uses standard code fences. These should be converted to ```python (or similar), and the {testoutput} block can be removed if it's intentionally empty.
Proposed fix
-```{testcode}
+```pythonAnd remove the empty testoutput block at the end (lines 98–101):
-{/* This testoutput is intentionally empty */}
-```{testoutput}
-:hide:
-```Also applies to: 99-101
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/design-docs/loss-functions.mdx` at line 27, Replace the
Sphinx/MyST doctest fences with standard MDX code fences: change the
```{testcode} block to a proper language fence such as ```python and remove the
empty ```{testoutput} block entirely; locate the occurrences labeled
`{testcode}` and `{testoutput}` in the loss-functions.mdx content and update the
opening fence symbol to a standard triple-backtick with a language (e.g.,
python) and delete the empty testoutput fence (the :hide: / ``` lines) so the
document uses valid Fern MDX code fences.
| ```{testcode} | ||
| import torch | ||
| from nemo_rl.distributed.batched_data_dict import BatchedDataDict | ||
| from nemo_rl.models.generation.interfaces import verify_right_padding | ||
|
|
||
| # For input data (BatchedDataDict containing input_ids and input_lengths) | ||
| input_data = BatchedDataDict({ | ||
| "input_ids": torch.tensor([ | ||
| [101, 2054, 2003, 0, 0], # Example input sequence | ||
| [101, 2054, 0, 0, 0] # Another input sequence | ||
| ]), | ||
| "input_lengths": torch.tensor([3, 2]) # Length of each sequence | ||
| }) | ||
|
|
||
| # Check if input data is properly right-padded | ||
| is_right_padded, error_msg = verify_right_padding(input_data, pad_value=0) | ||
|
|
||
| # For generation output data (BatchedDataDict containing output_ids and generation_lengths) | ||
| output_data = BatchedDataDict({ | ||
| "output_ids": torch.tensor([ | ||
| [101, 2054, 2003, 2001, 1996, 0, 0], # Example output sequence | ||
| [101, 2054, 2001, 4568, 0, 0, 0] # Another output sequence | ||
| ]), | ||
| "generation_lengths": torch.tensor([2, 2]), # Length of generated response | ||
| "unpadded_sequence_lengths": torch.tensor([5, 4]) # Total valid tokens | ||
| }) | ||
|
|
||
| # Check if output data is properly right-padded | ||
| is_right_padded, error_msg = verify_right_padding(output_data, pad_value=0) | ||
|
|
||
| if not is_right_padded: | ||
| print(f"Padding error: {error_msg}") | ||
| ``` | ||
|
|
||
| {/* This testoutput is intentionally empty */} | ||
| ```{testoutput} | ||
| :hide: | ||
| ``` |
There was a problem hiding this comment.
Sphinx doctest directives not converted to MDX syntax.
Lines 43 and 78–80 use Sphinx {testcode} / {testoutput} directive syntax, which won't render correctly in Fern MDX. Since this PR is migrating from Sphinx to Fern, these should be converted to standard fenced code blocks.
Proposed fix
-```{testcode}
+```python
import torch
from nemo_rl.distributed.batched_data_dict import BatchedDataDict
from nemo_rl.models.generation.interfaces import verify_right_paddingAnd remove the empty {testoutput} block entirely (lines 77–80):
-
-{/* This testoutput is intentionally empty */}
-```{testoutput}
-:hide:
-```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/design-docs/padding.mdx` around lines 43 - 80, Replace the
Sphinx `{testcode}`/`{testoutput}` directives with a standard fenced code block:
wrap the Python example (which imports BatchedDataDict and verify_right_padding
and constructs input_data/output_data) in a ```python ... ``` fence so it
renders in MDX, and remove the empty `{testoutput}` block entirely; ensure the
code still references verify_right_padding and BatchedDataDict exactly as in the
diff.
| --- | ||
| title: Documentation Development | ||
| description: "" | ||
| --- | ||
|
|
||
| - [Documentation Development](#documentation-development) | ||
| - [Build the Documentation](#build-the-documentation) | ||
| - [Live Building](#live-building) | ||
| - [Run Tests in Python Docstrings](#run-tests-in-python-docstrings) | ||
| - [Write Tests in Python Docstrings](#write-tests-in-python-docstrings) | ||
| - [Documentation Version](#documentation-version) | ||
|
|
||
| ## Build the Documentation | ||
|
|
||
| The following sections describe how to set up and build the NeMo RL documentation. | ||
|
|
||
| Switch to the documentation source folder and generate HTML output. | ||
|
|
||
| ```sh | ||
| cd docs/ | ||
| uv run --group docs sphinx-build . _build/html | ||
| ``` | ||
|
|
||
| * The resulting HTML files are generated in a `_build/html` folder that is created under the project `docs/` folder. | ||
| * The generated python API docs are placed in `apidocs` under the `docs/` folder. | ||
|
|
||
| ## Checking for Broken Links | ||
|
|
||
| To check for broken http links in the docs, run this command: | ||
|
|
||
| ```sh | ||
| cd docs/ | ||
| uv run --group docs sphinx-build --builder linkcheck . _build/linkcheck | ||
| ``` | ||
|
|
||
| It will output a JSON file at `_build/linkcheck/output.json` with links it found while building the | ||
| docs. Records will have a status of `broken` if the link is not reachable. The `docs/conf.py` file is | ||
| configured to ignore github links because the CI test will often experience rate limit errors. | ||
| Comment out the `linkcheck_ignore` variable there to check all the links. | ||
|
|
||
| ## Live Building | ||
|
|
||
| When writing documentation, it can be helpful to serve the documentation and have it update live while you edit. | ||
|
|
||
| To do so, run: | ||
|
|
||
| ```sh | ||
| cd docs/ | ||
| uv run --group docs sphinx-autobuild . _build/html --port 12345 --host 0.0.0.0 | ||
| ``` | ||
|
|
||
| Open a web browser and go to `http://${HOST_WHERE_SPHINX_COMMAND_RUN}:12345` to view the output. | ||
|
|
||
| ## Run Tests in Python Docstrings | ||
|
|
||
| We also run tests in our Python docstrings. You can run them with: | ||
|
|
||
| ```sh | ||
| cd docs/ | ||
| uv run --group docs sphinx-build -b doctest . _build/doctest | ||
| ``` | ||
|
|
||
| ## Write Tests in Python Docstrings | ||
|
|
||
| Any code in triple backtick blocks with the `{doctest}` directive will be tested. The format follows Python's doctest module syntax, where `>>>` indicates Python input and the following line shows the expected output. Here's an example: | ||
|
|
||
| ```python | ||
| def add(x: int, y: int) -> int: | ||
| """ | ||
| Adds two integers together. | ||
|
|
||
| Args: | ||
| x (int): The first integer to add. | ||
| y (int): The second integer to add. | ||
|
|
||
| Returns: | ||
| int: The sum of x and y. | ||
|
|
||
| Examples: | ||
| ```{doctest} | ||
| >>> from nemo_rl.made_up_package import add | ||
| >>> add(1, 2) | ||
| 3 | ||
| ``` | ||
|
|
||
| """ | ||
| return x + y | ||
| ``` | ||
|
|
||
| ## Documentation Version | ||
|
|
||
| The three files below control the version switcher. Before you attempt to publish a new version of the documentation, update these files to match the latest version numbers. | ||
|
|
||
| * docs/versions1.json | ||
| * docs/project.json | ||
| * docs/conf.py |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if Sphinx config files still exist in the repo
echo "=== Checking for Sphinx config files ==="
find . -name 'conf.py' -type f 2>/dev/null | grep -E 'docs/' | head -20
echo ""
find . -name 'versions1.json' -type f 2>/dev/null | head -20
echo ""
find . -name 'project.json' -type f 2>/dev/null | head -20
echo ""
echo "=== Checking directory structure ==="
ls -la fern/v0.5.0/pages/ | head -20
echo ""
echo "=== Checking if other .mdx files reference Sphinx or Fern ==="
fd -t f '.mdx' fern/v0.5.0/pages/ | head -10 | xargs -I {} sh -c 'echo "--- {} ---" && head -5 {}'
echo ""
echo "=== Looking for any Fern build or setup documentation ==="
rg -l 'fern' fern/v0.5.0/pages/ -t mdx 2>/dev/null
echo ""
echo "=== Check root level for docs or build config ==="
ls -la | grep -E 'docs|fern'Repository: NVIDIA-NeMo/RL
Length of output: 2481
🏁 Script executed:
#!/bin/bash
echo "=== Looking for Fern config files ==="
find . -name 'fern.config.json' -o -name 'fern.yml' -o -name 'fern.yaml' 2>/dev/null
echo ""
echo "=== Check if fern directory has any config ==="
ls -la fern/
echo ""
echo "=== Check fern/v0.5.0 structure ==="
ls -la fern/v0.5.0/
echo ""
echo "=== Look for README explaining docs setup ==="
find . -maxdepth 2 -name 'README*' -type f | xargs -I {} sh -c 'echo "--- {} ---" && head -30 {}'
echo ""
echo "=== Check docs/ directory structure ==="
ls -la docs/
</script>Repository: NVIDIA-NeMo/RL
Length of output: 8921
🏁 Script executed:
cat fern/README.mdRepository: NVIDIA-NeMo/RL
Length of output: 3201
Update documentation.mdx to clarify the distinction between Fern (user docs) and Sphinx (API docs).
This page is located in the Fern documentation structure but only describes the Sphinx workflow (sphinx-build, sphinx-autobuild, docs/conf.py, etc.). Contributors reading this will miss that Fern is now the primary documentation system. The fern/README.md clearly establishes Fern as the docs platform, with API docs built separately by Sphinx.
Either:
- Update the page to explain that Fern handles user-facing docs (use
fern docs devfor local preview) and Sphinx is used only for API generation, or - Move this to a separate "API Documentation" section and replace the main content with Fern-focused instructions.
Reference fern/README.md for the correct Fern workflow and migration steps.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/documentation.mdx` around lines 1 - 96, The Documentation
Development page currently describes only the Sphinx workflow (commands like
sphinx-build, sphinx-autobuild and files like docs/conf.py) but lives in the
Fern docs; update documentation.mdx (the "Documentation Development" page) to
clarify that Fern is the primary user-facing docs system and that Sphinx is used
only for API generation: add a short intro pointing to fern/README.md and the
Fern local preview command (fern docs dev) for authoring user docs, then either
(a) keep Sphinx instructions but label them as "API Documentation (Sphinx only)"
or (b) move the Sphinx content under a new "API Documentation" subsection and
replace the main content with Fern-focused instructions and links to
fern/README.md and the API build steps (sphinx-build, sphinx-autobuild,
docs/conf.py).
| > [!NOTE] | ||
| > When dynamic sampling is enabled, monitor the `filtered_reward` metric to track the average reward of the prompts with std > 0. | ||
|
|
||
| > [!NOTE] | ||
| > **Clip-Higher** and **Token-Level Policy Gradient Loss** are already supported in NeMo RL and can be configured through the `loss_fn` section of your experiment config: | ||
| > - Set `ratio_clip_max` to enable Clip-Higher (e.g., `ratio_clip_max: 0.28`) | ||
| > - Set `token_level_loss: true` to enable Token-Level Policy Gradient Loss | ||
| > | ||
| > See the full [DAPO example config](/../../examples/configs/recipes/llm/dapo-qwen2.5-7b.yaml) for reference. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Count how many MDX files use GitHub-style callouts
echo "=== Files using > [!NOTE] or > [!TIP] or > [!WARNING] ==="
rg -l '\> \[!' fern/ --glob '*.mdx' | head -20
echo ""
echo "=== Total count of files ==="
rg -l '\> \[!' fern/ --glob '*.mdx' | wc -l
echo ""
echo "=== Sample lines from dapo.mdx ==="
rg '\> \[!' fern/v0.5.0/pages/guides/dapo.mdx -A 2 -B 1Repository: NVIDIA-NeMo/RL
Length of output: 419
🌐 Web query:
Fern MDX callout syntax support GitHub-style [!NOTE] [!TIP] [!WARNING]
💡 Result:
Fern Docs (MDX) does not document/support the GitHub/Microsoft-style blockquote callouts syntax:
> [!NOTE]
> ...Instead, Fern’s supported callouts are MDX components, e.g.:
<Note>…</Note>
<Tip>…</Tip>
<Warning>…</Warning>(and also <Info>, <Success>, <Error>, <Launch>, <Check>), with optional title/icon props. [1]
Source(s)
[1] Fern Documentation – “Callout” component https://buildwithfern.com/learn/docs/writing-content/components/callouts
🏁 Script executed:
#!/bin/bash
# Read the specific file and lines mentioned
cd fern/v0.5.0/pages/guides/
wc -l dapo.mdx
echo "--- Lines 80-92 of dapo.mdx ---"
sed -n '80,92p' dapo.mdxRepository: NVIDIA-NeMo/RL
Length of output: 1022
Replace GitHub-style callout syntax with Fern MDX components.
Lines 82–90 use > [!NOTE] syntax, which Fern MDX does not support. This will render the callout markers as literal text in the final documentation. Use Fern's MDX callout components instead:
<Note>
When dynamic sampling is enabled, monitor the `filtered_reward` metric to track the average reward of the prompts with std > 0.
</Note>
<Note>
**Clip-Higher** and **Token-Level Policy Gradient Loss** are already supported in NeMo RL and can be configured through the `loss_fn` section of your experiment config:
- Set `ratio_clip_max` to enable Clip-Higher (e.g., `ratio_clip_max: 0.28`)
- Set `token_level_loss: true` to enable Token-Level Policy Gradient Loss
See the full [DAPO example config](/../../examples/configs/recipes/llm/dapo-qwen2.5-7b.yaml) for reference.
</Note>Fern MDX supports <Note>, <Tip>, <Warning>, <Info>, <Success>, <Error>, <Launch>, and <Check> components with optional title and icon props.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/guides/dapo.mdx` around lines 82 - 90, Replace the
GitHub-style callouts that begin with the literal string "> [!NOTE]" with Fern
MDX callout components: wrap the first note's text about monitoring the
`filtered_reward` metric in a <Note>...</Note> component and wrap the second
block describing Clip-Higher and Token-Level Policy Gradient Loss (including the
bullets for `ratio_clip_max` and `token_level_loss: true` and the DAPO example
config link) in a separate <Note>...</Note> component, preserving the original
wording and formatting.
| COMMAND="${COMMAND}" \ | ||
| CONTAINER="${CONTAINER}" \ | ||
| MOUNTS="${MOUNTS}" \ | ||
| WANDB_API_KEY=${WANDB_API_KEY} \ | ||
| sbatch \ | ||
| --nodes=32 \ | ||
| --account="${SLURM_ACCOUNT}" \ | ||
| --job-name="${SLURM_JOB_NAME}" \ | ||
| --partition="${SLURM_PARTITION}" \ | ||
| --time=4:0:0 \ | ||
| --gres=gpu:8 \ | ||
| ray.sub |
There was a problem hiding this comment.
ray.sub is referenced but never introduced — users won't know where to find it.
The launch script submits to Slurm via ray.sub, but this file isn't provided, linked, or explained anywhere in the guide. Users following these steps will hit an immediate error. Add either a link to where ray.sub lives in the repo or inline its contents (or a note that it's included in the cloned repo).
Additionally, the environment variables COMMAND, CONTAINER, MOUNTS, and WANDB_API_KEY are set as shell variables before sbatch, but whether they propagate into the job depends on Slurm's --export default (usually ALL, but not guaranteed). If ray.sub reads these as environment variables, consider explicitly passing them:
-COMMAND="${COMMAND}" \
-CONTAINER="${CONTAINER}" \
-MOUNTS="${MOUNTS}" \
-WANDB_API_KEY=${WANDB_API_KEY} \
-sbatch \
+sbatch \
+ --export=ALL,COMMAND="${COMMAND}",CONTAINER="${CONTAINER}",MOUNTS="${MOUNTS}",WANDB_API_KEY="${WANDB_API_KEY}" \📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| COMMAND="${COMMAND}" \ | |
| CONTAINER="${CONTAINER}" \ | |
| MOUNTS="${MOUNTS}" \ | |
| WANDB_API_KEY=${WANDB_API_KEY} \ | |
| sbatch \ | |
| --nodes=32 \ | |
| --account="${SLURM_ACCOUNT}" \ | |
| --job-name="${SLURM_JOB_NAME}" \ | |
| --partition="${SLURM_PARTITION}" \ | |
| --time=4:0:0 \ | |
| --gres=gpu:8 \ | |
| ray.sub | |
| sbatch \ | |
| --export=ALL,COMMAND="${COMMAND}",CONTAINER="${CONTAINER}",MOUNTS="${MOUNTS}",WANDB_API_KEY="${WANDB_API_KEY}" \ | |
| --nodes=32 \ | |
| --account="${SLURM_ACCOUNT}" \ | |
| --job-name="${SLURM_JOB_NAME}" \ | |
| --partition="${SLURM_PARTITION}" \ | |
| --time=4:0:0 \ | |
| --gres=gpu:8 \ | |
| ray.sub |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fern/v0.5.0/pages/guides/nemotron-3-nano.mdx` around lines 53 - 64, The guide
references an external Slurm submit script named "ray.sub" but never provides it
or explains where to get it, and it also relies on shell variables COMMAND,
CONTAINER, MOUNTS, and WANDB_API_KEY being visible inside the sbatch job; fix by
adding either a link to the repo location of ray.sub or inlining its contents in
the guide and explicitly documenting that ray.sub reads those vars, and modify
the sbatch invocation guidance to show using sbatch --export=ALL or
--export=COMMAND,CONTAINER,MOUNTS,WANDB_API_KEY (or adding EXPORT lines inside
ray.sub) so the variables are reliably passed into the job; reference the
ray.sub file and the sbatch invocation and the variable names when updating the
docs.
terrykong
left a comment
There was a problem hiding this comment.
hi @lbliii . Does this PR replace your previous doc pr?
Thanks for helping refactor our docs. Some questions:
- What is fern and how does it contrast with our current tooling for docs?
- This is an extremely large PR. The last PR took me a while to review the subtle things that an agent hallucinated, so I worry about doing a good job in such a large PR like this. Is there any way we can break this PR up?
- Why were the submodules bumped?
❌ Submodule Fast-Forward Check FailedCheck based on commit: 33e0de8 (PR #1975 from ❌ Submodules that need attention:Automodel: ❌ Commits have DIVERGED from a common ancestor Megatron-Bridge: ❌ PR branch is BEHIND main branch Megatron-LM: ❌ Commits have DIVERGED from a common ancestor Please ensure all submodule commits are fast-forwards of the main branch before merging. |
Hey! Basically, all of NVIDIA docs is migrating from Sphinx to Fern and I'm trying to stage some migration checkpoints across NeMo FW. So yes, this is actually going to replace the other PR. My intention with this one is to just convert everything 1:1 as much as possible. So no IA/content changes, just syntax etc. I need to do a few sweeps to validate that but wanted to get a buildable preview up asap. Here are some others: Some examples that have already gone live: I'm happy to break this PR up into phases. In the meantime, your team can use this branch/pr as a sandbox to get a feel for Fern. Fern is MDX based and comes with some improvements around publishing/previews and more integrated AI features for search/discovery. There are a few feature gaps, but they are working with us to close those. I'm also working on some react components to natively display notebooks in the docs (initial test of that is here) I did not mean to bump the module versions, I will revert that. Sorry! |
What does this PR do ?
initial staging / migration of docs to new tooling (fern).
installation of cli
run