v0.27.3:0 - cap image resolution (fix oversized-image 400); remove vision-check button
A 12MP photo expands past vLLM's ~4096-image-token limit -> 400. Cap via --mm-processor-kwargs max_pixels in the qwen36 recipe so big images auto- downscale server-side for every /v1 consumer (verified live: 400->200). Remove the v0.27.2 in-dashboard vision-check button per owner request; the vision badge already signals capability.
This commit is contained in:
+1
-84
@@ -4,7 +4,6 @@ const state = {
|
||||
models: {},
|
||||
defaults: {},
|
||||
current_model_key: null,
|
||||
vllm_model: null, // model id vLLM currently reports serving (for the vision check)
|
||||
swap_job_id: null,
|
||||
swap_eventsource: null,
|
||||
swap_started_at: null,
|
||||
@@ -121,11 +120,6 @@ function renderCards() {
|
||||
const recipeActions = m.needs_setup ? '' : `
|
||||
<button class="btn test-btn" data-test-key="${key}" title="Pre-flight check the launch command without starting the engine">Test</button>
|
||||
<button class="btn adv-btn" data-adv-key="${key}" title="Advanced settings">Advanced</button>`;
|
||||
// "Vision check" only makes sense for the model that's actually loaded, and
|
||||
// only if it can take images — send an image to it and see what it reads.
|
||||
const visionBtn = (isActive && (m.capabilities || []).includes('vision'))
|
||||
? `<button class="btn vision-btn" data-vision-key="${key}" title="Send an image to the running model (e.g. a business card) and see what it reads">Vision check</button>`
|
||||
: '';
|
||||
card.innerHTML = `
|
||||
<div class="name">${escapeHtml(m.display_name)}</div>
|
||||
<div class="meta">
|
||||
@@ -144,7 +138,7 @@ function renderCards() {
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="card-actions">
|
||||
${primaryBtn}${recipeActions}${visionBtn}
|
||||
${primaryBtn}${recipeActions}
|
||||
${trashBtn}
|
||||
</div>
|
||||
<div class="test-result hidden" data-test-result-for="${key}"></div>
|
||||
@@ -166,80 +160,6 @@ function renderCards() {
|
||||
for (const btn of root.querySelectorAll('[data-disk-del-key]')) {
|
||||
btn.addEventListener('click', () => openDiskDeleteDialog(btn.dataset.diskDelKey));
|
||||
}
|
||||
for (const btn of root.querySelectorAll('[data-vision-key]')) {
|
||||
btn.addEventListener('click', () => openVisionCheck(btn.dataset.visionKey));
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== vision check =====================
|
||||
|
||||
function openVisionCheck(key) {
|
||||
const m = state.models[key];
|
||||
el('#vc-model').textContent = m ? ` — ${m.display_name}` : '';
|
||||
el('#vc-file').value = '';
|
||||
el('#vc-preview').classList.add('hidden');
|
||||
el('#vc-preview').removeAttribute('src');
|
||||
const res = el('#vc-result');
|
||||
res.classList.add('hidden');
|
||||
res.textContent = '';
|
||||
el('#vision-dialog').showModal();
|
||||
}
|
||||
|
||||
function previewVisionImage() {
|
||||
const file = el('#vc-file').files[0];
|
||||
const img = el('#vc-preview');
|
||||
if (!file) { img.classList.add('hidden'); return; }
|
||||
img.src = URL.createObjectURL(file);
|
||||
img.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function readFileAsDataURL(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fr = new FileReader();
|
||||
fr.onload = () => resolve(fr.result);
|
||||
fr.onerror = () => reject(new Error('could not read the image file'));
|
||||
fr.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
async function runVisionCheck() {
|
||||
const file = el('#vc-file').files[0];
|
||||
const res = el('#vc-result');
|
||||
if (!file) { alert('Pick an image first.'); return; }
|
||||
const modelId = state.vllm_model;
|
||||
if (!modelId) { alert('No running model detected — switch to a model first.'); return; }
|
||||
const prompt = el('#vc-prompt').value.trim() || 'Describe this image.';
|
||||
const btn = el('#vc-run');
|
||||
btn.disabled = true; btn.textContent = 'Running…';
|
||||
res.classList.remove('hidden', 'fail');
|
||||
res.textContent = 'Sending the image to the model…';
|
||||
try {
|
||||
const dataUrl = await readFileAsDataURL(file);
|
||||
const r = await fetchJSON('/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model: modelId,
|
||||
max_tokens: 800,
|
||||
temperature: 0,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: prompt },
|
||||
{ type: 'image_url', image_url: { url: dataUrl } },
|
||||
],
|
||||
}],
|
||||
}),
|
||||
});
|
||||
const msg = r.choices && r.choices[0] && r.choices[0].message;
|
||||
const text = (msg && msg.content && msg.content.trim()) || '(model returned no text)';
|
||||
res.textContent = text;
|
||||
} catch (e) {
|
||||
res.classList.add('fail');
|
||||
res.textContent = 'Failed: ' + e.message;
|
||||
} finally {
|
||||
btn.disabled = false; btn.textContent = 'Run';
|
||||
}
|
||||
}
|
||||
|
||||
const trashIcon = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"></path><path d="M10 11v6"></path><path d="M14 11v6"></path><path d="M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"></path></svg>';
|
||||
@@ -1223,7 +1143,6 @@ async function pollStatus() {
|
||||
try {
|
||||
const status = await fetchJSON('/api/status');
|
||||
state.current_model_key = status.current_model_key;
|
||||
state.vllm_model = (status.vllm || {}).current_model || null;
|
||||
state.configured = status.configured;
|
||||
renderBanner(status);
|
||||
renderCurrent(status);
|
||||
@@ -2410,8 +2329,6 @@ async function init() {
|
||||
});
|
||||
el('#sshkey-close').addEventListener('click', () => el('#sshkey-dialog').close());
|
||||
el('#open-local').addEventListener('click', openLocalModelDialog);
|
||||
el('#vc-run').addEventListener('click', runVisionCheck);
|
||||
el('#vc-file').addEventListener('change', previewVisionImage);
|
||||
el('#lock-release').addEventListener('click', releaseLock);
|
||||
setupCatalogDialog();
|
||||
setupAdvancedDialog();
|
||||
|
||||
Reference in New Issue
Block a user