Changeset 0.14.0 (#67)
This commit is contained in:
@@ -402,32 +402,79 @@ function renderRoleConfig(containerEl, opts = {}) {
|
||||
|
||||
function filterModels(providerId, roleId) {
|
||||
const typeFilter = Roles.typeFor(roleId);
|
||||
return _models.filter(m =>
|
||||
m[pidField] === providerId &&
|
||||
(m.model_type || 'chat') === typeFilter
|
||||
);
|
||||
return _models.filter(m => {
|
||||
if (m[pidField] !== providerId) return false;
|
||||
const mt = m.model_type || '';
|
||||
// Embedding role: include models typed as 'embedding' OR untyped.
|
||||
// Many providers don't report type for embedding models.
|
||||
if (typeFilter === 'embedding') return !mt || mt === 'embedding';
|
||||
// Other roles: default untyped to 'chat' (existing behavior).
|
||||
return (mt || 'chat') === typeFilter;
|
||||
});
|
||||
}
|
||||
|
||||
function onProviderChange(roleId, slot) {
|
||||
const provSel = containerEl.querySelector(`[data-role-provider="${roleId}-${slot}"]`);
|
||||
const modelSel = containerEl.querySelector(`[data-role-model="${roleId}-${slot}"]`);
|
||||
const modelInput = containerEl.querySelector(`[data-role-model-input="${roleId}-${slot}"]`);
|
||||
const toggleBtn = containerEl.querySelector(`[data-role-model-toggle="${roleId}-${slot}"]`);
|
||||
if (!provSel || !modelSel) return;
|
||||
|
||||
modelSel.innerHTML = '<option value="">— select model —</option>';
|
||||
const provId = provSel.value;
|
||||
if (!provId) return;
|
||||
|
||||
filterModels(provId, roleId).forEach(m => {
|
||||
const models = filterModels(provId, roleId);
|
||||
models.forEach(m => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = m[midField];
|
||||
opt.textContent = m[mnameField] || m[midField];
|
||||
modelSel.appendChild(opt);
|
||||
});
|
||||
|
||||
// Auto-switch to manual entry if dropdown has no models.
|
||||
if (models.length === 0 && modelInput && toggleBtn) {
|
||||
modelSel.style.display = 'none';
|
||||
modelInput.style.display = '';
|
||||
toggleBtn.title = 'Switch to dropdown';
|
||||
toggleBtn.textContent = '▾';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleManualEntry(roleId, slot) {
|
||||
const modelSel = containerEl.querySelector(`[data-role-model="${roleId}-${slot}"]`);
|
||||
const modelInput = containerEl.querySelector(`[data-role-model-input="${roleId}-${slot}"]`);
|
||||
const toggleBtn = containerEl.querySelector(`[data-role-model-toggle="${roleId}-${slot}"]`);
|
||||
if (!modelSel || !modelInput || !toggleBtn) return;
|
||||
|
||||
const showingDropdown = modelSel.style.display !== 'none';
|
||||
if (showingDropdown) {
|
||||
// Switch to manual: copy dropdown value into input.
|
||||
modelInput.value = modelSel.value || modelInput.value;
|
||||
modelSel.style.display = 'none';
|
||||
modelInput.style.display = '';
|
||||
toggleBtn.title = 'Switch to dropdown';
|
||||
toggleBtn.textContent = '▾';
|
||||
} else {
|
||||
// Switch to dropdown: try to select the typed value.
|
||||
modelSel.style.display = '';
|
||||
modelInput.style.display = 'none';
|
||||
toggleBtn.title = 'Type model ID manually';
|
||||
toggleBtn.textContent = '✎';
|
||||
if (modelInput.value) {
|
||||
modelSel.value = modelInput.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBinding(roleId, slot) {
|
||||
const prov = containerEl.querySelector(`[data-role-provider="${roleId}-${slot}"]`)?.value;
|
||||
const model = containerEl.querySelector(`[data-role-model="${roleId}-${slot}"]`)?.value;
|
||||
const modelSel = containerEl.querySelector(`[data-role-model="${roleId}-${slot}"]`);
|
||||
const modelInput = containerEl.querySelector(`[data-role-model-input="${roleId}-${slot}"]`);
|
||||
// Use manual input if it's visible, otherwise use dropdown.
|
||||
const model = (modelInput && modelInput.style.display !== 'none')
|
||||
? modelInput.value.trim()
|
||||
: (modelSel?.value || '');
|
||||
return (prov && model) ? { provider_config_id: prov, model_id: model } : null;
|
||||
}
|
||||
|
||||
@@ -478,11 +525,15 @@ function renderRoleConfig(containerEl, opts = {}) {
|
||||
`<option value="${p.id}"${p.id === selectedProv ? ' selected' : ''}>${esc(p.name)}</option>`
|
||||
).join('');
|
||||
|
||||
const modelOptions = selectedProv
|
||||
? filterModels(selectedProv, roleId).map(m =>
|
||||
`<option value="${m[midField]}"${m[midField] === selectedModel ? ' selected' : ''}>${esc(m[mnameField] || m[midField])}</option>`
|
||||
).join('')
|
||||
: '';
|
||||
const dropdownModels = selectedProv ? filterModels(selectedProv, roleId) : [];
|
||||
const modelOptions = dropdownModels.map(m =>
|
||||
`<option value="${m[midField]}"${m[midField] === selectedModel ? ' selected' : ''}>${esc(m[mnameField] || m[midField])}</option>`
|
||||
).join('');
|
||||
|
||||
// Show manual entry if: saved model ID not in dropdown, or dropdown is empty with a provider selected.
|
||||
const savedNotInDropdown = selectedModel && selectedProv &&
|
||||
!dropdownModels.some(m => m[midField] === selectedModel);
|
||||
const showManual = savedNotInDropdown || (selectedProv && dropdownModels.length === 0);
|
||||
|
||||
return `
|
||||
<div class="form-row" style="gap:8px;align-items:center${slotName === 'fallback' ? ';margin-top:6px' : ''}">
|
||||
@@ -491,10 +542,17 @@ function renderRoleConfig(containerEl, opts = {}) {
|
||||
<option value="">${noneLabel}</option>
|
||||
${provOptions}
|
||||
</select>
|
||||
<select data-role-model="${roleId}-${slotName}" style="min-width:200px">
|
||||
<select data-role-model="${roleId}-${slotName}" style="min-width:200px${showManual ? ';display:none' : ''}">
|
||||
<option value="">— select model —</option>
|
||||
${modelOptions}
|
||||
</select>
|
||||
<input type="text" data-role-model-input="${roleId}-${slotName}"
|
||||
placeholder="Model ID (e.g. text-embedding-3-small)"
|
||||
value="${esc(showManual ? selectedModel : '')}"
|
||||
style="min-width:200px;font-size:12px${showManual ? '' : ';display:none'}">
|
||||
<button type="button" class="btn-small" data-role-model-toggle="${roleId}-${slotName}"
|
||||
title="${showManual ? 'Switch to dropdown' : 'Type model ID manually'}"
|
||||
style="padding:2px 6px;font-size:12px;line-height:1">${showManual ? '▾' : '✎'}</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -553,6 +611,12 @@ function renderRoleConfig(containerEl, opts = {}) {
|
||||
if (testBtn) { handleTest(testBtn.dataset.roleTest); return; }
|
||||
const clearBtn = e.target.closest('[data-role-clear]');
|
||||
if (clearBtn) { handleClear(clearBtn.dataset.roleClear); return; }
|
||||
const toggleBtn = e.target.closest('[data-role-model-toggle]');
|
||||
if (toggleBtn) {
|
||||
const [roleId, slot] = toggleBtn.dataset.roleModelToggle.split('-');
|
||||
toggleManualEntry(roleId, slot);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return { refresh };
|
||||
|
||||
Reference in New Issue
Block a user