Add admin LLM model setting; rename Pi to LLM in the UI; drop table Review column
Test and deploy / test-and-deploy (push) Successful in 49s

- New server/llm.js consolidates ModelRuntime + model selection: admin
  setting (app_settings.llm_model) > LLM_MODEL/PREFILL_MODEL env > first
  available; used by both prefill and roast evaluation
- GET/PUT /api/admin/llm lists configured models and stores the choice
  (validated against the list; empty = auto; audited); admin page gains
  an LLM section with a model picker
- All user-facing 'Pi agent' wording is now 'LLM'; no_model error message
  no longer references the pi CLI
- /roasts table: Review column removed (review lives in the detail view)

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Shane Maynard
2026-08-08 22:12:42 -04:00
co-authored by Claude Fable 5
parent eb82263ead
commit 5efaeb63c9
14 changed files with 344 additions and 87 deletions
+60
View File
@@ -93,6 +93,64 @@ function wireSignupToggle() {
});
}
async function loadLlm() {
const select = document.getElementById("llm-model-select");
const note = document.getElementById("llm-model-note");
try {
const { current, models, modelsError } = await api("/api/admin/llm");
const auto = document.createElement("option");
auto.value = "";
auto.textContent = "Auto (first available model)";
select.replaceChildren(
auto,
...models.map((model) => {
const option = document.createElement("option");
option.value = model.key;
option.textContent = `${model.name} (${model.provider})`;
return option;
}),
);
// A previously-saved model that is no longer configured must still show as selected
// rather than silently displaying Auto while the server keeps trying the saved one.
if (current && !models.some((model) => model.key === current)) {
const missing = document.createElement("option");
missing.value = current;
missing.textContent = `${current} (no longer configured)`;
select.append(missing);
}
select.value = current;
note.textContent = modelsError
? "No models are configured on the server — reviews and prefill will fail until one is set up."
: current
? ""
: models.length
? `Auto currently resolves to ${models[0].name} (${models[0].provider}).`
: "";
} catch {
note.textContent = "Could not load LLM settings.";
}
}
function wireLlmSave() {
const button = document.getElementById("llm-model-save");
button.addEventListener("click", async () => {
button.disabled = true;
try {
const model = document.getElementById("llm-model-select").value;
await api("/api/admin/llm", {
method: "PUT",
body: JSON.stringify({ model }),
});
showToast(model ? "LLM model saved." : "LLM model set to auto.");
await loadLlm();
} catch (error) {
showToast(error.message, "fail");
} finally {
button.disabled = false;
}
});
}
function roleBadge(user) {
return user.role === "admin"
? `<span class="badge badge-admin">Admin</span>`
@@ -317,12 +375,14 @@ async function init() {
if (!user) return;
currentUserId = user.id;
wireSignupToggle();
wireLlmSave();
await Promise.all([
loadMetrics(),
loadResetLinks(),
loadUsers(),
loadPlans(),
loadAudit(),
loadLlm(),
]);
}