From 63b4ad2e9c0cd075d6fcc8ce1dea131888321bf4 Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Sun, 14 Jun 2026 22:47:26 +0900 Subject: [PATCH] Cookbook/Dependencies: Pip/uv vs Docker variant toggle on recipe panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each recipe catalog entry now carries two variants: variants.pip → uv pip install … variants.docker → docker pull A small 'Install via' pill row in the panel toggles between them (default = Pip/uv per the user's preference). Switching variant or changing the model re-renders the
 via _refreshRecipePre(); the
display text drops the 'source venv/bin/activate' prefix for Docker
since docker pull doesn't need a venv. Run honours the active variant
so picking Docker queues 'docker pull …' as the tmux task.
---
 static/js/cookbook-deps-recipes.js | 63 +++++++++++++++----------
 static/js/cookbook.js              | 75 ++++++++++++++++++++++--------
 2 files changed, 94 insertions(+), 44 deletions(-)

diff --git a/static/js/cookbook-deps-recipes.js b/static/js/cookbook-deps-recipes.js
index 36ecf5bbd..afb1b7287 100644
--- a/static/js/cookbook-deps-recipes.js
+++ b/static/js/cookbook-deps-recipes.js
@@ -5,33 +5,37 @@
 // Entries are matched first-hit; put the more specific patterns ABOVE the
 // generic fallback for that backend.
 
-// Recipes hold ONLY the install command(s). The renderer prepends a
-// `source /bin/activate` line for context using the active server's
-// configured envPath; the Run button reuses env_prefix to activate that
-// same venv before executing, so the install lands in your existing
-// environment rather than a fresh `.venv` in some random CWD.
+// Recipes carry two variants per entry:
+//   variants.pip    → install into the configured venv via uv/pip
+//   variants.docker → pull the official container image
+//
+// The renderer prepends a `source /bin/activate` for the pip variant
+// (env_prefix handles activation for Run). The docker variant skips the
+// activate line — `docker pull` doesn't need a venv.
+
 const _RECIPES = [
   // ── vllm ──────────────────────────────────────────────────────────────
-  // MiniMax M2/M2.7 — same generic vllm install for now; kept as its own
-  // entry so future model-specific patches (FP8 quants, custom kernels)
-  // land in one obvious place without touching the catch-all.
+  // MiniMax M2/M2.7 — same as the generic vllm install/image for now;
+  // kept as its own entry so future model-specific patches land in one
+  // obvious place without touching the catch-all.
   {
     backend: 'vllm',
     label: 'MiniMax M2 / M2.7',
     match: (m) => /minimax[-_]?m\s?2(\.7)?/i.test(m || ''),
-    commands: [
-      'uv pip install -U vllm --torch-backend auto',
-    ],
+    variants: {
+      pip:    { commands: ['uv pip install -U vllm --torch-backend auto'] },
+      docker: { commands: ['docker pull vllm/vllm-openai:latest'] },
+    },
   },
-  // Generic vllm fallback — auto-resolves the right torch backend (CUDA
-  // 12.x / 12.4 / ROCm) at install time so users don't have to know.
+  // Generic vllm fallback.
   {
     backend: 'vllm',
     label: 'Any vLLM model',
     match: () => true,
-    commands: [
-      'uv pip install -U vllm --torch-backend auto',
-    ],
+    variants: {
+      pip:    { commands: ['uv pip install -U vllm --torch-backend auto'] },
+      docker: { commands: ['docker pull vllm/vllm-openai:latest'] },
+    },
   },
 
   // ── sglang ────────────────────────────────────────────────────────────
@@ -39,24 +43,35 @@ const _RECIPES = [
     backend: 'sglang',
     label: 'Any SGLang model',
     match: () => true,
-    commands: [
-      'uv pip install -U "sglang[all]" --torch-backend auto',
-    ],
+    variants: {
+      pip:    { commands: ['uv pip install -U "sglang[all]" --torch-backend auto'] },
+      docker: { commands: ['docker pull lmsysorg/sglang:latest'] },
+    },
   },
 
   // ── llama.cpp ─────────────────────────────────────────────────────────
-  // The cookbook-side rebuild path covers this for users who already have
-  // the engine compiled — but for a fresh box, surface a sane install.
   {
     backend: 'llama_cpp',
     label: 'Any GGUF model',
     match: () => true,
-    commands: [
-      'CMAKE_ARGS="-DGGML_CUDA=on" uv pip install -U "llama-cpp-python[server]"',
-    ],
+    variants: {
+      pip:    { commands: ['CMAKE_ARGS="-DGGML_CUDA=on" uv pip install -U "llama-cpp-python[server]"'] },
+      docker: { commands: ['docker pull ghcr.io/ggerganov/llama.cpp:server-cuda'] },
+    },
   },
 ];
 
+export const RECIPE_VARIANTS = ['pip', 'docker'];
+export const RECIPE_DEFAULT_VARIANT = 'pip';
+
+// Get the commands array for a recipe + variant. Falls back to pip when
+// the requested variant isn't defined for the recipe.
+export function recipeCommands(recipe, variant) {
+  if (!recipe) return [];
+  const v = (recipe.variants || {})[variant] || (recipe.variants || {}).pip;
+  return (v && v.commands) || [];
+}
+
 // Backends we surface a recipe panel for. Other rows in the Dependencies
 // list keep the existing flat Install/Reinstall button without an expand
 // affordance.
diff --git a/static/js/cookbook.js b/static/js/cookbook.js
index 584ec2ff0..f3efc0079 100644
--- a/static/js/cookbook.js
+++ b/static/js/cookbook.js
@@ -8,7 +8,7 @@ import spinnerModule from './spinner.js';
 import { providerLogo } from './providers.js';
 import { makeWindowDraggable } from './windowDrag.js';
 import { _diagnose, _showDiagnosis, _clearDiagnosis, _runQuickCmd, ERROR_PATTERNS } from './cookbook-diagnosis.js';
-import { RECIPE_BACKENDS, recipesForBackend, pickRecipe } from './cookbook-deps-recipes.js';
+import { RECIPE_BACKENDS, recipesForBackend, pickRecipe, recipeCommands, RECIPE_DEFAULT_VARIANT } from './cookbook-deps-recipes.js';
 import { _hwfitCache, _hwfitDebounce, _hwfitFetch, _hwfitInit, _hwfitRenderList, _hwfitRenderHw, _renderGpuToggles, _expandModelRow, _fitColors, _hwfitColumns, _cachedModelIds, _gpuToggleTotal, _resetGpuToggleState } from './cookbook-hwfit.js';
 
 // Sub-modules
@@ -834,10 +834,12 @@ async function _fetchDependencies() {
         + recipePanel;
     };
 
-    // Prepend the configured venv's activate line (when one is set) so the
-    // user sees the full sequence they'd need to paste, while Run keeps
-    // using env_prefix to activate the same venv before the pip command.
-    function _recipeDisplayText(commands) {
+    // Prepend the configured venv's activate line (pip variant only) so
+    // the user sees a paste-ready sequence; Run keeps using env_prefix to
+    // activate the same venv before the pip command. Docker variant skips
+    // the activate line — `docker pull` doesn't need a venv.
+    function _recipeDisplayText(commands, variant) {
+      if (variant === 'docker') return commands.join('\n');
       const envPath = (_envState.envPath || '').replace(/\/+$/, '');
       const activate = envPath
         ? `source ${envPath}${envPath.endsWith('/bin/activate') ? '' : '/bin/activate'}`
@@ -864,12 +866,21 @@ async function _fetchDependencies() {
       const opts = modelOptions + otherOpt;
       // Initial recipe: the generic fallback (matches first time, no model id).
       const initial = pickRecipe(backend, '') || candidates[0];
-      return `