feat(tools): DB-backed LLM engine admin (owner-only)

Add an owner-gated dashboard to remap any tool/tier's model live without a
code push. Overrides live in dbn_tool_engine_config (dobetternorge_maindb)
and are consulted by dbnToolsResolveToolRun() + dbnToolsReviewerModel();
no row = unchanged code/.env behaviour (fully back-compatible).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 14:55:17 +02:00
parent f270a32056
commit 2f05b84b0f
7 changed files with 546 additions and 2 deletions
+66
View File
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../includes/bootstrap.php';
if (!dbnToolsIsAuthenticated()) {
dbnToolsRequirePageAuth($_SERVER['REQUEST_URI'] ?? '/dashboard/llm-engines.php');
}
if (!dbnToolsIsOwner()) {
http_response_code(403);
echo '<!doctype html><meta charset="utf-8"><title>Owner access required</title>'
. '<p style="font-family:sans-serif;max-width:540px;margin:4rem auto;">'
. 'Owner access required. <a href="/dashboard/">Back to dashboard</a></p>';
exit;
}
$dashboardPage = 'llm-engines';
$dashboardTitle = 'LLM Engines';
$dashboardLead = 'Remap which model each tool/tier uses — live, no code push. Overrides fall back to the code/.env default when cleared.';
require_once __DIR__ . '/../includes/layout_dashboard.php';
?>
<section class="dash-card">
<div class="dash-card__head">
<h2>Routing context</h2>
<div class="dash-card__actions">
<button class="dash-btn" type="button" id="engRefresh">↻ Refresh</button>
</div>
</div>
<p id="engContext" style="margin:0; max-width:74ch; line-height:1.6; font-size:0.9rem; color:rgba(22,19,15,0.7);">Loading…</p>
</section>
<section class="dash-card">
<div class="dash-card__head">
<h2>Tier engines (Quick / Pro)</h2>
</div>
<p style="margin-top:0; max-width:74ch; line-height:1.6; font-size:0.86rem; color:rgba(22,19,15,0.6);">
The <code>*</code> rows set the default for every tier tool. A per-tool row overrides just that tool. Credits stay tied to the tier, not the chosen engine.
</p>
<div id="engTierPanel" class="eng-table"><div class="dms-loading"></div></div>
</section>
<section class="dash-card">
<div class="dash-card__head">
<h2>Reviewer personas (legal-analysis track)</h2>
</div>
<div id="engPersonaPanel" class="eng-table"><div class="dms-loading"></div></div>
</section>
<style>
.eng-table { background:#fff; border:1px solid var(--dms-stroke,#e3ddd2); border-radius:var(--dms-radius,10px); overflow:hidden; }
.eng-row { display:grid; grid-template-columns:1.4fr 0.9fr 1.5fr auto; gap:12px; padding:10px 16px; border-bottom:1px solid var(--dms-stroke-soft,#efe9dd); font-size:13px; align-items:center; }
.eng-row:last-child { border-bottom:0; }
.eng-row__tool { font-weight:600; color:var(--dms-navy,#16130f); }
.eng-row__scope { font-family:ui-monospace,Menlo,monospace; font-size:11px; color:rgba(22,19,15,0.55); }
.eng-row__def { font-size:11px; color:rgba(22,19,15,0.55); margin-top:2px; }
.eng-row select { width:100%; padding:5px 8px; border:1px solid var(--dms-stroke,#d9d2c5); border-radius:6px; font-size:12px; background:#fff; }
.eng-row__actions { display:flex; gap:6px; align-items:center; }
.eng-badge { font-size:10px; font-weight:600; padding:2px 7px; border-radius:999px; background:rgba(180,138,44,0.16); color:#6c5212; }
.eng-badge--default { background:rgba(22,19,15,0.06); color:rgba(22,19,15,0.5); }
.eng-meta { font-size:10px; color:rgba(22,19,15,0.45); }
.eng-clear { font-size:11px; padding:4px 8px; }
.eng-head { display:grid; grid-template-columns:1.4fr 0.9fr 1.5fr auto; gap:12px; padding:8px 16px; font-size:10px; text-transform:uppercase; letter-spacing:0.05em; color:rgba(22,19,15,0.4); background:rgba(22,19,15,0.02); border-bottom:1px solid var(--dms-stroke-soft,#efe9dd); }
</style>
<script src="/assets/js/dashboard/llm-engines.js" defer></script>
<?php require_once __DIR__ . '/../includes/layout_dashboard_footer.php'; ?>