Skip to content

[Prototype] Add interactive deployment config generator#318

Draft
mgoin wants to merge 1 commit intomainfrom
add-interactive-config-generator
Draft

[Prototype] Add interactive deployment config generator#318
mgoin wants to merge 1 commit intomainfrom
add-interactive-config-generator

Conversation

@mgoin
Copy link
Copy Markdown
Member

@mgoin mgoin commented Apr 7, 2026

Summary

  • Adds a reusable interactive configuration selector widget for vLLM deployment recipes, inspired by SGLang Cookbook's ConfigGenerator
  • Built with vanilla HTML/CSS/JS that works natively with MkDocs Material (no React or build step needed)
  • Includes a proof-of-concept in the Llama 3.3 70B recipe with options for hardware platform, quantization, tensor parallelism, and prefix caching
  • Supports light/dark mode via MkDocs Material CSS variables, dynamic option dependencies (e.g. NVFP4 auto-disabled on Hopper), and copy-to-clipboard
image

How to add to other recipes

Recipe authors just add a <div> and <script> block (~40 lines) defining model-specific options and a generateCommand function. The base component (assets/config-generator.js + assets/config-generator.css) handles all the rendering and state management.

Test plan

  • Run mkdocs serve and verify the Llama 3.3 70B recipe shows the interactive selector
  • Click through hardware/quantization/TP options and verify the generated command updates correctly
  • Verify NVFP4 is disabled when Hopper is selected
  • Verify dark mode styling works
  • Verify copy button works

🤖 Generated with Claude Code

Add a reusable interactive configuration selector widget (similar to
SGLang Cookbook's ConfigGenerator) that lets users pick hardware platform,
quantization, tensor parallelism, and other options to auto-generate the
correct vllm serve command.

Includes a proof-of-concept integration in the Llama 3.3 70B recipe with
options for Blackwell/Hopper hardware, NVFP4/FP8 quantization, TP size,
and prefix caching. Other recipes can adopt the widget by adding a small
HTML/JS block that defines model-specific options and a generateCommand
function.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an interactive command generator for Llama 3.3-70B deployment, allowing users to select hardware, quantization, and parallelism settings to generate a vLLM serve command. The implementation includes a new vanilla JavaScript utility, custom CSS, and updates to the documentation and mkdocs configuration. Review feedback focuses on ensuring the generated command correctly overrides YAML defaults for prefix caching, optimizing the initialization logic in the JavaScript class, and adding safety checks for the clipboard API.

Comment on lines +102 to +104
if (values.prefixCaching === 'disabled') {
cmd += ' \\\n --no-enable-prefix-caching';
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current logic only adds the --no-enable-prefix-caching flag when "Disabled" is selected. However, the provided YAML configuration files (Llama3.3_Blackwell.yaml and Llama3.3_Hopper.yaml) already have no-enable-prefix-caching: true set. This means that selecting "Enabled" in the interactive tool will not actually enable prefix caching, as the YAML setting will persist. To ensure the interactive tool works as expected, you should explicitly add the --enable-prefix-caching flag when "Enabled" is selected, as CLI arguments typically override configuration file settings in vLLM.

Suggested change
if (values.prefixCaching === 'disabled') {
cmd += ' \\\n --no-enable-prefix-caching';
}
if (values.prefixCaching === 'enabled') {
cmd += ' \\\n --enable-prefix-caching';
} else {
cmd += ' \\\n --no-enable-prefix-caching';
}

Comment on lines +35 to +54
_getInitialState() {
const state = {};
for (const [key, option] of Object.entries(this.config.options)) {
let items = option.items;
if (option.getDynamicItems) {
// Bootstrap: build default values from static items first
const defaults = {};
for (const [k, opt] of Object.entries(this.config.options)) {
if (opt.items && opt.items.length > 0) {
const d = opt.items.find(i => i.default);
defaults[k] = d ? d.id : opt.items[0].id;
}
}
items = option.getDynamicItems(defaults);
}
const defaultItem = items && items.find(i => i.default);
state[key] = defaultItem ? defaultItem.id : (items && items[0] ? items[0].id : '');
}
return state;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The defaults object is currently recalculated inside the loop for every option that uses getDynamicItems. It is more efficient to pre-calculate these defaults once at the beginning of the method.

Suggested change
_getInitialState() {
const state = {};
for (const [key, option] of Object.entries(this.config.options)) {
let items = option.items;
if (option.getDynamicItems) {
// Bootstrap: build default values from static items first
const defaults = {};
for (const [k, opt] of Object.entries(this.config.options)) {
if (opt.items && opt.items.length > 0) {
const d = opt.items.find(i => i.default);
defaults[k] = d ? d.id : opt.items[0].id;
}
}
items = option.getDynamicItems(defaults);
}
const defaultItem = items && items.find(i => i.default);
state[key] = defaultItem ? defaultItem.id : (items && items[0] ? items[0].id : '');
}
return state;
}
_getInitialState() {
const state = {};
const defaults = {};
for (const [k, opt] of Object.entries(this.config.options)) {
if (opt.items && opt.items.length > 0) {
const d = opt.items.find(i => i.default);
defaults[k] = d ? d.id : opt.items[0].id;
}
}
for (const [key, option] of Object.entries(this.config.options)) {
let items = option.items;
if (option.getDynamicItems) {
items = option.getDynamicItems(defaults);
}
const defaultItem = items && items.find(i => i.default);
state[key] = defaultItem ? defaultItem.id : (items && items[0] ? items[0].id : '');
}
return state;
}

Comment on lines +155 to +160
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(command).then(() => {
copyBtn.textContent = 'Copied!';
setTimeout(() => { copyBtn.textContent = 'Copy'; }, 1500);
});
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Accessing navigator.clipboard without checking for its existence can lead to runtime errors in non-secure contexts (HTTP) or older browsers where the API is not available.

Suggested change
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(command).then(() => {
copyBtn.textContent = 'Copied!';
setTimeout(() => { copyBtn.textContent = 'Copy'; }, 1500);
});
});
copyBtn.addEventListener('click', () => {
if (!navigator.clipboard) return;
navigator.clipboard.writeText(command).then(() => {
copyBtn.textContent = 'Copied!';
setTimeout(() => { copyBtn.textContent = 'Copy'; }, 1500);
});
});

Copy link
Copy Markdown

@ProExpertProg ProExpertProg left a comment

Choose a reason for hiding this comment

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

Looks nice!

@mgoin mgoin marked this pull request as draft April 7, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants