diff --git a/frontend/src/components/ModelsAdmin.css b/frontend/src/components/ModelsAdmin.css index 0692cf1..2fe96d9 100644 --- a/frontend/src/components/ModelsAdmin.css +++ b/frontend/src/components/ModelsAdmin.css @@ -113,3 +113,21 @@ } @media (min-width: 700px) { .mdl-embed-row input, .mdl-embed-row select { font-size: 0.84rem; } } .mdl-embed-note { margin: 10px 0 0; font-size: 0.78rem; color: var(--text-muted); } + +/* Typing an id the proxy does not list. */ +.mdl-add { display: flex; gap: 8px; margin: 10px 0; } +.mdl-add input { + flex: 1; min-width: 0; padding: 6px 10px; font: inherit; font-size: .85rem; + color: var(--text); background: var(--input-bg); + border: 1px solid var(--border); border-radius: 6px; +} + +/* Off, not gone: the row stays, dimmed, with its settings intact. */ +.mdl-current li.is-off code { opacity: .55; text-decoration: line-through; } +.mdl-badge.is-off { background: var(--border); color: var(--text-muted); } +.mdl-toggle { + padding: 3px 10px; font-size: .78rem; font-weight: 600; cursor: pointer; + color: var(--text-muted); background: none; + border: 1px solid var(--border); border-radius: 999px; +} +.mdl-toggle:hover { color: var(--primary); border-color: var(--primary); } diff --git a/frontend/src/components/ModelsAdmin.jsx b/frontend/src/components/ModelsAdmin.jsx index e594ae9..4fcd6a8 100644 --- a/frontend/src/components/ModelsAdmin.jsx +++ b/frontend/src/components/ModelsAdmin.jsx @@ -56,6 +56,7 @@ export default function ModelsAdmin() { const [finding, setFinding] = useState(false) const [findError, setFindError] = useState('') const [filter, setFilter] = useState('') + const [typed, setTyped] = useState('') const [allowFor, setAllowFor] = useState('teach') // Embeddings: one model for the whole site. @@ -112,6 +113,28 @@ export default function ModelsAdmin() { finally { setFinding(false) } } + //: Off rather than gone. The row keeps its task, its default flag and any + //: key on it, so turning a model back on is one press rather than finding + //: it in the proxy list again and setting it up from scratch. + const setActive = (model, on) => act(`active:${model.id}`, + async () => { + const res = await api.put(`/admin/models/${model.id}`, { is_active: on }) + setModels(prev => prev.map(row => (row.id === model.id ? res.data : row))) + }, + on ? 'Could not turn that model on' : 'Could not turn that model off', + `${model.model_id} is ${on ? 'on' : 'off'} for ${JOBS.find(j => j.key === allowFor)?.label}.`) + + //: A model id the proxy does not list. Discovery reads /model/info, and a + //: gateway can route a model it does not advertise there — a new alias, a + //: provider added by hand — so the roster must not be limited to what the + //: list happens to say. + const allowTyped = () => { + const id = typed.trim() + if (!id) return + setTyped('') + allow(id) + } + const allow = (modelId) => act(`allow:${modelId}`, async () => { const res = await api.post('/admin/models', @@ -289,9 +312,19 @@ export default function ModelsAdmin() {
{model.model_id}
{truthy(model.is_default) && in use}
+ {!truthy(model.is_active) && off}
+ {/* Off, not gone. A model taken out while the proxy is
+ misbehaving used to have to be removed and then found
+ and allowed again afterwards, losing whether it was the
+ default and any key set on it. */}
+