Skip to content

Commit 1fdc48d

Browse files
feat: Implement configurable independent community sub-sites with modular navigation base on verl (#111)
1 parent 627597b commit 1fdc48d

File tree

9 files changed

+406
-19
lines changed

9 files changed

+406
-19
lines changed

.gitmodules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[submodule "_repos/verl"]
22
path = _repos/verl
33
url = https://github.com/volcengine/verl.git
4+
branch = main
45
[submodule "_repos/LLaMA-Factory"]
56
path = _repos/LLaMA-Factory
67
url = https://github.com/the-seeds/LLaMA-Factory-Doc.git

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ ONNXRUNTIME_CANN_MD_LOCAL := sources/_generated/sources/onnxruntime/quick_start.
3232
help:
3333
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
3434

35-
.PHONY: help Makefile copy-docs clean-submodules fetch-config sync-onnxruntime-doc
35+
.PHONY: help Makefile copy-docs clean-submodules fetch-config sync-onnxruntime-doc init-submodules
3636

3737
# Fetch ascend config (always run to ensure freshness)
3838
.PHONY: $(ASCEND_CONFIG)
@@ -58,13 +58,13 @@ sync-onnxruntime-doc:
5858
@rm -f "$(ONNXRUNTIME_CANN_MD_LOCAL).tmp"
5959
@echo "Synced to $(ONNXRUNTIME_CANN_MD_LOCAL)"
6060

61-
# Initialize submodules if not exists (use pinned commits for reproducibility)
62-
_repos/verl _repos/VeOmni _repos/LLaMA-Factory _repos/ms-swift:
63-
@echo "Initializing submodules..."
64-
@git submodule update --init
61+
# Initialize submodules (always run to handle empty dirs left by git clone)
62+
init-submodules:
63+
@git submodule sync --recursive
64+
@git submodule update --init --remote
6565

6666
# Copy documentation from submodules
67-
copy-docs: _repos/verl _repos/VeOmni _repos/LLaMA-Factory _repos/ms-swift
67+
copy-docs: init-submodules
6868
@echo "Preparing generated docs directory..."
6969
@mkdir -p $(GENERATED_DOCS)
7070

_repos/verl

Submodule verl updated from 07d4033 to 77476af

_static/custom.css

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,34 @@ body.onnxruntime-quickstart-page .rst-content .highlight {
362362
}
363363

364364

365-
/* card-icon 样式已移至 index.rst 中统一管理 */
365+
/* =========================================================
366+
独立社区侧边栏专属样式
367+
========================================================= */
368+
369+
#independent-sidebar .custom-sidebar-caption {
370+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
371+
font-size: 85%;
372+
color: #0066cc;
373+
font-weight: 700;
374+
text-transform: uppercase;
375+
margin-top: 1.5em;
376+
margin-bottom: 0.5em;
377+
padding-left: 15px;
378+
letter-spacing: 0.5px;
379+
}
380+
381+
#independent-sidebar ul {
382+
margin-bottom: 0 !important;
383+
}
384+
385+
/* 覆盖菜单基础项的 padding,使其靠左贴齐 */
386+
#independent-sidebar ul li.toctree-l1 > a {
387+
padding-left: 15px !important;
388+
}
389+
390+
#independent-sidebar ul li.toctree-l1.current > a {
391+
background: #e6f0fa; /* 当前选中项高亮背景色 */
392+
color: #0066cc;
393+
border-left: 4px solid #0066cc;
394+
padding-left: 11px !important; /* 减去边框宽度保持对齐 */
395+
}

_templates/layout.html

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
{% extends "!layout.html" %}
2+
3+
{% block menu %}
4+
{% if is_independent %}
5+
<!-- 1. 将原本庞大的全局菜单隐藏起来,作为数据源 -->
6+
<div id="temp-menu-holder" style="display: none;">
7+
{{ super() }}
8+
</div>
9+
10+
<!-- 2. 独立站点独立侧边栏容器(直接插入在原菜单位置) -->
11+
<div id="independent-sidebar"></div>
12+
13+
<script>
14+
document.addEventListener('DOMContentLoaded', function() {
15+
const mapping = {{ comm_config.get('sidebar_mapping', {}) | tojson | safe }};
16+
const commId = '{{ current_community_id }}';
17+
18+
// 获取数据源和目标容器
19+
const globalMenu = document.getElementById('temp-menu-holder');
20+
const targetContainer = document.getElementById('independent-sidebar');
21+
22+
if (!globalMenu) return;
23+
24+
// 步骤 A: 找到当前社区的主节点 (通常带有 .current 类)
25+
let activeL1 = globalMenu.querySelector('li.toctree-l1.current');
26+
27+
// 兜底策略: 如果没有 .current (如强制刷新等边缘情况),通过链接匹配
28+
if (!activeL1 || !activeL1.querySelector('a').href.includes('/sources/' + commId + '/')) {
29+
const links = globalMenu.querySelectorAll('li.toctree-l1 > a');
30+
for (let i = 0; i < links.length; i++) {
31+
if (links[i].href.includes('/sources/' + commId + '/')) {
32+
activeL1 = links[i].closest('li');
33+
break;
34+
}
35+
}
36+
}
37+
38+
if (activeL1) {
39+
// 步骤 B: 找到属于这个社区的所有子页面 (<ul>)
40+
const subMenu = activeL1.querySelector('ul');
41+
42+
if (subMenu && subMenu.children.length > 0) {
43+
let currentGroup = null;
44+
let currentUl = null;
45+
46+
// 步骤 C: 遍历所有的子页面,将它们分组并插入到独立侧边栏中
47+
const items = Array.from(subMenu.children);
48+
items.forEach(item => {
49+
if (item.tagName.toLowerCase() !== 'li') return;
50+
51+
const link = item.querySelector('a');
52+
if (!link) return;
53+
54+
const fullUrl = link.href;
55+
let matchedFolder = null;
56+
57+
// 匹配 conf.py 中配置的文件夹路径
58+
// 使用更精确的匹配:sources/{commId}/{folder}/ 或 sources/_generated/sources/{commId}/{folder}/
59+
for (const folder in mapping) {
60+
const pattern1 = `/sources/${commId}/${folder}/`;
61+
const pattern2 = `/sources/_generated/sources/${commId}/${folder}/`;
62+
if (fullUrl.includes(pattern1) || fullUrl.includes(pattern2)) {
63+
matchedFolder = folder;
64+
break;
65+
}
66+
}
67+
68+
// 如果有的文件放在根目录没匹配到,归入 default
69+
if (!matchedFolder) {
70+
matchedFolder = currentGroup || 'default';
71+
}
72+
73+
// 步骤 D: 遇到新分组时,创建大标题和新的 <ul>
74+
if (currentGroup !== matchedFolder) {
75+
if (mapping[matchedFolder]) {
76+
const caption = document.createElement('p');
77+
caption.className = 'caption custom-sidebar-caption';
78+
caption.innerHTML = `<span class="caption-text">${mapping[matchedFolder]}</span>`;
79+
targetContainer.appendChild(caption);
80+
}
81+
82+
currentUl = document.createElement('ul');
83+
targetContainer.appendChild(currentUl);
84+
currentGroup = matchedFolder;
85+
}
86+
87+
// 步骤 E: 智能动态提升整棵树的级数,完美支持长文档的内部 H2/H3 目录显示
88+
// 找出该节点及其内部所有的级联列表项
89+
const allTocItems = [item, ...item.querySelectorAll('[class*="toctree-l"]')];
90+
allTocItems.forEach(el => {
91+
// 将 toctree-l(X) 动态替换为 toctree-l(X-1)
92+
el.className = el.className.replace(/toctree-l(\d+)/g, function(match, level) {
93+
const newLevel = parseInt(level, 10) - 1;
94+
return 'toctree-l' + newLevel;
95+
});
96+
});
97+
98+
// 挂载到对应独立社区的菜单中
99+
if (currentUl) {
100+
currentUl.appendChild(item);
101+
}
102+
});
103+
104+
// 步骤 F: 彻底删除隐蔽的全局菜单
105+
globalMenu.remove();
106+
return;
107+
}
108+
}
109+
110+
// 异常兜底: 如果出现极端的解析失败(比如网络延迟),取消隐藏,确保文档菜单可用
111+
globalMenu.style.display = 'block';
112+
});
113+
</script>
114+
{% else %}
115+
<!-- 非独立社区页面,直接按普通 Sphinx 逻辑渲染 -->
116+
{{ super() }}
117+
{% endif %}
118+
{% endblock %}

conf.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,32 @@
3030
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3131
# ones.
3232
extensions = [
33-
'recommonmark',
3433
'sphinx.ext.autodoc',
3534
'sphinxext.remoteliteralinclude',
3635
'sphinx_copybutton',
3736
'sphinx_markdown_tables',
3837
"sphinx_design",
38+
'myst_parser',
3939
]
4040

41+
# 这一行非常重要,它允许 MyST 处理复杂的标题层级
42+
myst_heading_anchors = 3
43+
44+
# 允许在 RST 中嵌入 Markdown 内容
45+
myst_enable_extensions = [
46+
"colon_fence",
47+
"html_image",
48+
]
49+
50+
# conf.py
51+
html_theme_options = {
52+
# 仅显示当前页面所属的导航树,不显示全局无关节点
53+
'collapse_navigation': True,
54+
'navigation_depth': 4,
55+
'includehidden': True,
56+
'titles_only': False,
57+
}
58+
4159
# 模板路径设置
4260
templates_path = ['_templates']
4361
autosummary_generate = True
@@ -180,6 +198,53 @@ def generate_api_doc():
180198
with open(rst_filename, 'w', encoding='utf-8') as f:
181199
f.write(rendered_content)
182200

201+
# =========================================================
202+
# 独立社区页面全局配置 (核心机制:高度可配置化)
203+
# =========================================================
204+
html_context = {
205+
'independent_communities': {
206+
'verl': {
207+
'display_name': 'verl',
208+
'sidebar_mapping': {
209+
'quick_start': '🚀 快速入门',
210+
'features': '📚 特性指南',
211+
'profiling': '⚡ 调优指南',
212+
'examples': '📊 应用与调优实践',
213+
'faq': '🔆 故障排查(FAQ)',
214+
'contribution_guide': '🔧 开源开发'
215+
}
216+
},
217+
}
218+
}
219+
220+
# 【新增核心代码】:Sphinx 页面上下文注入 Hook
221+
def update_page_context(app, pagename, templatename, context, doctree):
222+
"""
223+
拦截页面渲染,判断当前路径是否属于独立社区。
224+
如果是,向 Jinja 模板注入 is_independent 和对应的 comm_config 变量。
225+
支持两种路径格式:
226+
- sources/verl/... (手写页面)
227+
- sources/_generated/sources/verl/... (从子模块拉取的页面)
228+
"""
229+
communities = context.get('independent_communities', {})
230+
context['is_independent'] = False
231+
232+
for comm_id, comm_info in communities.items():
233+
# 匹配两种路径格式
234+
if pagename.startswith(f"sources/{comm_id}") or pagename.startswith(f"sources/_generated/sources/{comm_id}"):
235+
context['is_independent'] = True
236+
context['current_community_id'] = comm_id
237+
context['comm_config'] = comm_info
238+
break
239+
240+
def setup(app):
241+
app.add_css_file('custom.css')
242+
app.add_js_file('package_info.js')
243+
app.add_js_file('statistics.js')
244+
245+
# 【新增 Hook 注册】
246+
app.connect('html-page-context', update_page_context)
247+
183248
# 在 Sphinx 构建之前调用该函数生成 API 文档
184249
try:
185250
generate_api_doc()

index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
<div class="project-card">
208208
<div class="card-top"><div class="card-icon" style="background-image: url('_static/images/volcano.png')"></div><h3 class="card-title">verl</h3></div>
209209
<p class="card-desc">用于 LLM 的强化学习训练库,适配昇腾并行计算方案。</p>
210-
<div class="card-footer"><a href="https://github.com/volcengine/verl">官方链接</a><span class="split">|</span><a href="sources/_generated/sources/verl/ascend_quick_start.html">安装指南</a><span class="split">|</span><a href="sources/_generated/sources/verl/ascend_quick_start.html">快速上手</a></div>
210+
<div class="card-footer"><a href="https://github.com/volcengine/verl">官方链接</a><span class="split">|</span><a href="sources/_generated/sources/verl/quick_start/ascend_quick_start.html">安装指南</a><span class="split">|</span><a href="sources/_generated/sources/verl/quick_start/ascend_quick_start.html">快速上手</a></div>
211211
</div>
212212

213213
</div>

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ sphinx_markdown_tables
88
sphinx-design
99
torch==2.5.1+cpu; platform_machine == "x86_64"
1010
torch==2.5.1; platform_machine == "aarch64"
11-
torch_npu==2.5.1rc1; sys_platform == "linux"
11+
torch_npu==2.5.1rc1;sys_platform == "linux"
12+
myst_parser

0 commit comments

Comments
 (0)