diff --git a/static/js/cookbook-diagnosis.js b/static/js/cookbook-diagnosis.js index 698fd89be..ddd79d2ea 100644 --- a/static/js/cookbook-diagnosis.js +++ b/static/js/cookbook-diagnosis.js @@ -101,22 +101,30 @@ function _openCookbookDependencies(pkgName = '', opts = {}) { row.classList.add('cookbook-pkg-flash'); setTimeout(() => row.classList.remove('cookbook-pkg-flash'), 1800); // Pre-flight deep link: auto-expand the recipe panel + pre-select - // the model the user was trying to launch. + // the model the user was trying to launch. The dropdown values are + // now full model ids (sourced from _cachedModelIds), so we match by + // exact value first, then fall back to a substring match. if (opts.expandRecipe) { const caret = row.querySelector('[data-dep-recipe-toggle]'); if (caret && caret.getAttribute('aria-expanded') !== 'true') caret.click(); if (opts.model) { const sel = document.querySelector(`[data-dep-recipe-pick="${CSS.escape(opts.expandRecipe)}"]`); if (sel) { - // Find first matching recipe; if none, leave on default. + const wanted = String(opts.model); + let matched = false; for (let i = 0; i < sel.options.length; i++) { - const label = (sel.options[i].textContent || '').toLowerCase(); - if (/minimax/i.test(opts.model) && /minimax/i.test(label)) { - sel.value = String(i); - sel.dispatchEvent(new Event('change')); - break; + if (sel.options[i].value === wanted) { + sel.value = wanted; matched = true; break; } } + if (!matched) { + for (let i = 0; i < sel.options.length; i++) { + if (sel.options[i].value && wanted.includes(sel.options[i].value)) { + sel.value = sel.options[i].value; matched = true; break; + } + } + } + if (matched) sel.dispatchEvent(new Event('change')); } } } diff --git a/static/js/cookbook.js b/static/js/cookbook.js index 81fd48b8e..a5b1ff5b6 100644 --- a/static/js/cookbook.js +++ b/static/js/cookbook.js @@ -836,11 +836,23 @@ async function _fetchDependencies() { // Per-backend recipe panel (model picker + commands + Copy/Run). // Lives directly below the row it expands and starts collapsed. + // The model picker lists every downloaded model from _cachedModelIds + // (the same set the Launch tab uses); pickRecipe() then finds the + // best-matching recipe for whatever the user selects, with the + // backend's generic entry as the fallback. function _recipePanelHtml(backend) { const candidates = recipesForBackend(backend); if (!candidates.length) return ''; - const opts = candidates.map((r, i) => ``).join(''); - const initial = candidates[0]; + const downloadedIds = _cachedModelIds ? Array.from(_cachedModelIds).sort() : []; + const modelOptions = downloadedIds.length + ? downloadedIds.map(id => ``).join('') + : ''; + // "Other" entry: user types/pastes an id, OR uses the generic fallback + // when no models have been downloaded yet. + const otherOpt = ``; + const opts = modelOptions + otherOpt; + // Initial recipe: the generic fallback (matches first time, no model id). + const initial = pickRecipe(backend, '') || candidates[0]; return `