Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions docs/en/_static/css/config_generator.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/* ================================================================
LMDeploy Interactive Configuration Generator – pill-bar layout
Matches the SGLang Cookbook segmented-control style
================================================================ */

/* Wrapper: full content width, no card background */
.cg-wrapper {
width: 100%;
margin: 0;
padding: 0;
}

/* ── Dimension row ─────────────────────────────────────────────── */
.cg-row {
margin-bottom: 1.25em;
}

.cg-label {
font-weight: 600;
font-size: 0.95em;
margin-bottom: 0.4em;
color: var(--pst-color-text-base, #24292e);
}

/* ── Pill bar (segmented control) ──────────────────────────────── */
.cg-pill-bar {
display: flex;
flex-wrap: wrap;
width: 100%;
border: 1px solid var(--pst-color-border, #d1d5db);
border-radius: 6px;
overflow: hidden;
background: var(--pst-color-surface, #ffffff);
}

.cg-pill {
flex: 1 1 0;
min-width: 0;
padding: 0.55em 0.4em;
margin: 0;
border: none;
border-right: 1px solid var(--pst-color-border, #d1d5db);
background: transparent;
color: var(--pst-color-text-base, #24292e);
font-size: 0.88em;
font-weight: 500;
cursor: pointer;
text-align: center;
transition: background 0.15s ease, color 0.15s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
}

.cg-pill:last-child {
border-right: none;
}

.cg-pill:hover {
background: rgba(3, 102, 214, 0.08);
}

.cg-pill.active {
background: #0366d6;
color: #ffffff;
font-weight: 600;
}

.cg-pill.active:hover {
background: #0256c2;
}

/* ── Command output section ────────────────────────────────────── */
.cg-command-section {
margin-top: 1.5em;
}

.cg-command-label {
font-weight: 600;
font-size: 0.95em;
margin-bottom: 0.4em;
color: var(--pst-color-text-base, #24292e);
}

.cg-command-box {
background: #1e1e1e;
border-radius: 6px;
padding: 1em 1em 1em 1.2em;
position: relative;
width: 100%;
box-sizing: border-box;
}

.cg-command-box pre {
margin: 0;
padding: 0;
padding-right: 4.5em; /* space for the copy button */
background: transparent;
overflow-x: auto;
}

.cg-command-box code {
color: #d4d4d4;
font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', monospace;
font-size: 0.88em;
line-height: 1.6;
white-space: pre;
}

.cg-copy-btn {
position: absolute;
top: 0.6em;
right: 0.6em;
background: #0366d6;
color: #ffffff;
border: none;
border-radius: 4px;
padding: 0.35em 0.9em;
cursor: pointer;
font-size: 0.8em;
font-weight: 500;
transition: background 0.2s ease;
}

.cg-copy-btn:hover {
background: #0256c2;
}

.cg-copy-btn:active {
background: #014a9e;
}

/* ── Responsive: stack pills on narrow screens ─────────────────── */
@media (max-width: 640px) {
.cg-pill-bar {
flex-direction: column;
}

.cg-pill {
border-right: none;
border-bottom: 1px solid var(--pst-color-border, #d1d5db);
}

.cg-pill:last-child {
border-bottom: none;
}
}

/* ── Dark-mode overrides (sphinx-book-theme data-theme) ────────── */
html[data-theme="dark"] .cg-label,
html[data-theme="dark"] .cg-command-label {
color: #f0f6fc;
}

html[data-theme="dark"] .cg-pill-bar {
border-color: #30363d;
background: #161b22;
}

html[data-theme="dark"] .cg-pill {
color: #c9d1d9;
border-color: #30363d;
}

html[data-theme="dark"] .cg-pill:hover {
background: rgba(88, 166, 255, 0.12);
}

html[data-theme="dark"] .cg-pill.active {
background: #1f6feb;
color: #ffffff;
}

html[data-theme="dark"] .cg-command-box {
background: #0d1117;
}

html[data-theme="dark"] .cg-command-box code {
color: #c9d1d9;
}

/* Also handle prefers-color-scheme for themes without data-theme */
@media (prefers-color-scheme: dark) {
.cg-label,
.cg-command-label {
color: #f0f6fc;
}

.cg-pill-bar {
border-color: #30363d;
background: #161b22;
}

.cg-pill {
color: #c9d1d9;
border-color: #30363d;
}

.cg-pill:hover {
background: rgba(88, 166, 255, 0.12);
}

.cg-pill.active {
background: #1f6feb;
color: #ffffff;
}

.cg-command-box {
background: #0d1117;
}

.cg-command-box code {
Comment on lines +185 to +213
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @media (prefers-color-scheme: dark) block can conflict with the theme's manual light/dark toggle. If a user has their OS set to dark mode but has manually selected the light theme in sphinx_book_theme (which sets data-theme="light" on <html>), the bare selectors inside this media query will still apply dark colors to the config generator, while the rest of the page stays light-themed. Consider scoping these rules to only apply when data-theme is not explicitly set (e.g., html:not([data-theme]) .cg-label) or removing this fallback block entirely since sphinx_book_theme always sets data-theme.

Suggested change
.cg-label,
.cg-command-label {
color: #f0f6fc;
}
.cg-pill-bar {
border-color: #30363d;
background: #161b22;
}
.cg-pill {
color: #c9d1d9;
border-color: #30363d;
}
.cg-pill:hover {
background: rgba(88, 166, 255, 0.12);
}
.cg-pill.active {
background: #1f6feb;
color: #ffffff;
}
.cg-command-box {
background: #0d1117;
}
.cg-command-box code {
html:not([data-theme]) .cg-label,
html:not([data-theme]) .cg-command-label {
color: #f0f6fc;
}
html:not([data-theme]) .cg-pill-bar {
border-color: #30363d;
background: #161b22;
}
html:not([data-theme]) .cg-pill {
color: #c9d1d9;
border-color: #30363d;
}
html:not([data-theme]) .cg-pill:hover {
background: rgba(88, 166, 255, 0.12);
}
html:not([data-theme]) .cg-pill.active {
background: #1f6feb;
color: #ffffff;
}
html:not([data-theme]) .cg-command-box {
background: #0d1117;
}
html:not([data-theme]) .cg-command-box code {

Copilot uses AI. Check for mistakes.
color: #c9d1d9;
}
}
165 changes: 165 additions & 0 deletions docs/en/_static/js/config_generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// LMDeploy Interactive Configuration Generator — Generic Engine
// Model-specific configurations are loaded from js/models/*.js via
// the window.LMDeployModelConfigs global registry.
(function() {
'use strict';

function initConfigGenerator() {
var container = document.getElementById('lmdeploy-config-generator');
if (!container) return;

// ── Read model config from registry ──────────────────────────
var configKey = container.getAttribute('data-model-config') || 'qwen3';
var configs = window.LMDeployModelConfigs || {};
var config = configs[configKey];
if (!config) {
container.textContent = 'Unknown model config: ' + configKey +
'. Available: ' + Object.keys(configs).join(', ');
return;
}

// ── TP estimation (generic) ─────────────────────────────────
function getRecommendedTP(sel) {
var mem = (config.gpuMem || {})[sel.hardware] || 80;
var need = (config.modelMem || {})[sel.model_size] || 16;
if (sel.quantization === 'awq' || sel.quantization === 'gptq') {
need *= 0.3;
} else if (sel.quantization === 'fp8') {
need *= 0.55;
}
var tp = 1;
while (tp * mem < need * 1.15 && tp < 8) {
tp *= 2;
}
return tp;
Comment on lines +30 to +34
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TP estimation caps at 8 (while (tp < 8)), but several models in the configs (e.g., DeepSeek-V3 at 1300GB, GLM-5 at 1400GB) require far more than 8×80G = 640GB of GPU memory even for BF16 weights. When the user selects such a model with A100(80G) or H800(80G), the generated command will show --tp 8 which is clearly insufficient, potentially misleading users. Consider adding a warning or note in the generated output when tp * mem < need * 1.15 even at tp=8, indicating that the selected configuration requires more than 8 GPUs.

Copilot uses AI. Check for mistakes.
}

// ── Generate command ────────────────────────────────────────
function generateCommand() {
var sel = {};
container.querySelectorAll('.cg-pill-bar').forEach(function(bar) {
var key = bar.getAttribute('data-key');
var active = bar.querySelector('.cg-pill.active');
if (active) sel[key] = active.getAttribute('data-value');
});

var modelPath = config.buildModelPath(sel);
var tp = getRecommendedTP(sel);
var parts = ['lmdeploy serve api_server ' + modelPath];

if (tp > 1) parts.push('--tp ' + tp);

var extraFlags = config.buildExtraFlags ? config.buildExtraFlags(sel) : [];
parts = parts.concat(extraFlags);

if (parts.length <= 2) return parts.join(' ');
return parts[0] + ' \\\n' +
parts.slice(1).map(function(p) { return ' ' + p; }).join(' \\\n');
}

// ── Update command display ──────────────────────────────────
function updateCommand() {
var el = container.querySelector('.cg-generated-command');
if (el) el.textContent = generateCommand();
}

// ── Render a single dimension row ───────────────────────────
function renderDimension(dim) {
var row = document.createElement('div');
row.className = 'cg-row';

var label = document.createElement('div');
label.className = 'cg-label';
label.textContent = dim.label;
row.appendChild(label);

var bar = document.createElement('div');
bar.className = 'cg-pill-bar';
bar.setAttribute('data-key', dim.key);

dim.options.forEach(function(opt) {
var pill = document.createElement('button');
pill.className = 'cg-pill';
pill.setAttribute('data-value', opt.value);
pill.textContent = opt.label;
if (opt.value === dim.default) pill.classList.add('active');

pill.addEventListener('click', function() {
bar.querySelectorAll('.cg-pill').forEach(function(p) {
p.classList.remove('active');
});
pill.classList.add('active');
updateCommand();
});

bar.appendChild(pill);
});

row.appendChild(bar);
return row;
}

// ── Build the full UI ───────────────────────────────────────
var wrapper = document.createElement('div');
wrapper.className = 'cg-wrapper';

config.dimensions.forEach(function(dim) {
wrapper.appendChild(renderDimension(dim));
});

// Command output section
var cmdSection = document.createElement('div');
cmdSection.className = 'cg-command-section';

var cmdLabel = document.createElement('div');
cmdLabel.className = 'cg-command-label';
cmdLabel.textContent = 'Generated Command';
cmdSection.appendChild(cmdLabel);

var cmdBox = document.createElement('div');
cmdBox.className = 'cg-command-box';

var pre = document.createElement('pre');
var code = document.createElement('code');
code.className = 'cg-generated-command';
pre.appendChild(code);
cmdBox.appendChild(pre);

var copyBtn = document.createElement('button');
copyBtn.className = 'cg-copy-btn';
copyBtn.textContent = 'Copy';
copyBtn.addEventListener('click', function() {
var text = code.textContent;
navigator.clipboard.writeText(text).then(function() {
copyBtn.textContent = 'Copied!';
setTimeout(function() { copyBtn.textContent = 'Copy'; }, 2000);
}).catch(function() {
// Fallback for older browsers
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
copyBtn.textContent = 'Copied!';
setTimeout(function() { copyBtn.textContent = 'Copy'; }, 2000);
});
});
cmdBox.appendChild(copyBtn);

cmdSection.appendChild(cmdBox);
wrapper.appendChild(cmdSection);

container.appendChild(wrapper);
updateCommand();
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initConfigGenerator);
} else {
initConfigGenerator();
}
})();
Loading