feat: add whisper-server gguf source selector

parent 12596f2b
...@@ -101,7 +101,14 @@ ...@@ -101,7 +101,14 @@
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.75rem"> <div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.75rem">
<input id="ws-model-id" class="form-input" placeholder="whisper-vulkan-base"> <input id="ws-model-id" class="form-input" placeholder="whisper-vulkan-base">
<input id="ws-server-path" class="form-input" placeholder="/usr/local/bin/whisper-server"> <input id="ws-server-path" class="form-input" placeholder="/usr/local/bin/whisper-server">
<input id="ws-model-path" class="form-input" placeholder="/models/ggml-base.bin"> <select id="ws-model-source" class="form-input" onchange="toggleWhisperModelSource()">
<option value="cached-gguf">Downloaded GGUF</option>
<option value="manual-path">Manual path</option>
</select>
<select id="ws-gguf-select" class="form-input">
<option value="">Select downloaded GGUF</option>
</select>
<input id="ws-model-path" class="form-input" placeholder="Manual path: /models/ggml-base.bin" style="display:none">
<input id="ws-port" class="form-input" type="number" value="8744" min="1" max="65535"> <input id="ws-port" class="form-input" type="number" value="8744" min="1" max="65535">
<input id="ws-gpu-device" class="form-input" type="number" value="0" min="0"> <input id="ws-gpu-device" class="form-input" type="number" value="0" min="0">
<select id="ws-load-mode" class="form-input"> <select id="ws-load-mode" class="form-input">
...@@ -1068,6 +1075,11 @@ async function loadCachedModels(){ ...@@ -1068,6 +1075,11 @@ async function loadCachedModels(){
// GGUF files // GGUF files
const gguf = d.gguf||[]; const gguf = d.gguf||[];
const ggufSelect = document.getElementById('ws-gguf-select');
if(ggufSelect){
ggufSelect.innerHTML = '<option value="">Select downloaded GGUF</option>'+
gguf.map(f=>`<option value="${esc(f.path)}">${esc(f.filename)}</option>`).join('');
}
document.getElementById('gguf-file-badge').textContent = gguf.length ? `(${gguf.length})` : ''; document.getElementById('gguf-file-badge').textContent = gguf.length ? `(${gguf.length})` : '';
if(!gguf.length){ if(!gguf.length){
ggufEl.innerHTML = '<span class="muted small">No GGUF files cached.</span>'; ggufEl.innerHTML = '<span class="muted small">No GGUF files cached.</span>';
...@@ -1108,6 +1120,16 @@ async function loadCachedModels(){ ...@@ -1108,6 +1120,16 @@ async function loadCachedModels(){
} }
} }
function toggleWhisperModelSource(){
const source = document.getElementById('ws-model-source')?.value || 'cached-gguf';
const ggufSelect = document.getElementById('ws-gguf-select');
const modelPath = document.getElementById('ws-model-path');
if(!ggufSelect || !modelPath) return;
const useCached = source === 'cached-gguf';
ggufSelect.style.display = useCached ? '' : 'none';
modelPath.style.display = useCached ? 'none' : '';
}
let _loadedKeys = new Set(); let _loadedKeys = new Set();
async function refreshLoadedStatus(){ async function refreshLoadedStatus(){
...@@ -1321,12 +1343,16 @@ async function saveModelConfig(){ ...@@ -1321,12 +1343,16 @@ async function saveModelConfig(){
async function addWhisperServerModel(){ async function addWhisperServerModel(){
const usedVram = parseFloat(document.getElementById('ws-used-vram').value); const usedVram = parseFloat(document.getElementById('ws-used-vram').value);
const modelSource = document.getElementById('ws-model-source').value;
const payload = { const payload = {
model_id: document.getElementById('ws-model-id').value.trim(), model_id: document.getElementById('ws-model-id').value.trim(),
model_type: 'audio_models', model_type: 'audio_models',
backend: 'whisper-server', backend: 'whisper-server',
model_source: modelSource,
server_path: document.getElementById('ws-server-path').value.trim(), server_path: document.getElementById('ws-server-path').value.trim(),
model_path: document.getElementById('ws-model-path').value.trim() || null, model_path: (modelSource === 'cached-gguf'
? document.getElementById('ws-gguf-select').value
: document.getElementById('ws-model-path').value.trim()) || null,
port: parseInt(document.getElementById('ws-port').value, 10) || 8744, port: parseInt(document.getElementById('ws-port').value, 10) || 8744,
gpu_device: parseInt(document.getElementById('ws-gpu-device').value, 10) || 0, gpu_device: parseInt(document.getElementById('ws-gpu-device').value, 10) || 0,
load_mode: document.getElementById('ws-load-mode').value, load_mode: document.getElementById('ws-load-mode').value,
...@@ -1344,6 +1370,8 @@ async function addWhisperServerModel(){ ...@@ -1344,6 +1370,8 @@ async function addWhisperServerModel(){
}catch(e){ alert('Error: '+e.message); } }catch(e){ alert('Error: '+e.message); }
} }
toggleWhisperModelSource();
async function loadModel(idx){ async function loadModel(idx){
const m = _localModels[idx]; const m = _localModels[idx];
// Find the button and show loading state // Find the button and show loading state
......
...@@ -401,6 +401,17 @@ def test_models_template_contains_whisper_server_add_model_form(): ...@@ -401,6 +401,17 @@ def test_models_template_contains_whisper_server_add_model_form():
assert "Add model" in template assert "Add model" in template
assert "ws-model-id" in template assert "ws-model-id" in template
assert "ws-server-path" in template assert "ws-server-path" in template
assert "Downloaded GGUF" in template
assert "Manual path" in template
assert "ws-model-source" in template
assert "ws-gguf-select" in template
def test_models_template_preserves_whisper_server_manual_path_controls():
template = Path("codai/admin/templates/models.html").read_text()
assert "ws-model-path" in template
assert "Manual path" in template
def test_settings_template_no_longer_contains_whisper_server_section(): def test_settings_template_no_longer_contains_whisper_server_section():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment