Add three chat profiles: Varaverk Assistant, General Chat, Code Sketcher
Explicit buttons rather than an automatic router. Misclassifying a Varaverk question as chat produces a confident invention about the user's system, which is exactly what retrieval exists to prevent — with buttons there is no hidden heuristic to be wrong and the strict profile is the default you land on. Only Varaverk Assistant retrieves; the other two would be carrying passages that cannot help write a folder-copy script. Memory goes to all three, since that is what lets chat know the setup without claiming authority over it. History depth is per profile and set server-side: retrieval costs ~2500 of 16384, so the profiles that skip it can hold a real conversation. Code Sketcher is told to flag flags it is unsure of, after it invented rsync --no-overwrite.
This commit is contained in:
@@ -83,6 +83,16 @@ if (is_dir('/var/log/varaverk')) {
|
||||
.vv-ai-warn { color:#ffb74d !important; }
|
||||
.vv-ai-bad { color:#e57 !important; }
|
||||
|
||||
/* ── Profiles ───────────────────────────────────────────────────────────── */
|
||||
.vv-ai-profiles { display:flex; gap:6px; align-items:center; flex-wrap:wrap; }
|
||||
.vv-ai-prof { background:#0e0e0e; border:1px solid #262626; color:#5a5a5a; font-size:11px;
|
||||
padding:5px 12px; border-radius:4px; cursor:pointer; font-family:inherit; }
|
||||
.vv-ai-prof:hover { color:#8a8a8a; border-color:#333; }
|
||||
.vv-ai-prof.active { background:#152238; border-color:#2d4a6a; color:#9bd; }
|
||||
.vv-ai-prof-hint { font-size:10px; color:#4a4a4a; margin-left:6px; flex:1; min-width:180px; }
|
||||
.vv-ai-switch { text-align:center; font-size:10px; color:#3a3a3a; margin:10px 0;
|
||||
border-top:1px dashed #1e1e1e; padding-top:8px; }
|
||||
|
||||
/* ── Chat ───────────────────────────────────────────────────────────────── */
|
||||
.vv-ai-chat { border:1px solid #262626; border-radius:6px; background:#0b0b0b;
|
||||
min-height:340px; max-height:60vh; overflow-y:auto; padding:14px; }
|
||||
@@ -180,6 +190,13 @@ if (is_dir('/var/log/varaverk')) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vv-ai-profiles">
|
||||
<button class="vv-ai-prof active" data-prof="varaverk" type="button">Varaverk Assistant</button>
|
||||
<button class="vv-ai-prof" data-prof="chat" type="button">General Chat</button>
|
||||
<button class="vv-ai-prof" data-prof="code" type="button">Code Sketcher</button>
|
||||
<span class="vv-ai-prof-hint" id="vv-ai-prof-hint"></span>
|
||||
</div>
|
||||
|
||||
<div class="vv-ai-chat" id="vv-ai-chat">
|
||||
<div class="vv-ai-empty">
|
||||
Ask Varaverk about itself.<br>
|
||||
@@ -238,11 +255,23 @@ if (is_dir('/var/log/varaverk')) {
|
||||
<script>
|
||||
(function () {
|
||||
const API = '/plugins/varaverk/api/ai.php';
|
||||
const MAXTURN = 3;
|
||||
const POLL_MS = 1200;
|
||||
const POLL_CEIL = 300000; // stop polling a worker that never wrote a terminal state
|
||||
|
||||
let history = []; // {role, content} — trimmed to MAXTURN pairs
|
||||
// Server-side is the authority on retrieval and history depth; these are for the UI only.
|
||||
// Always starts on varaverk — the strict profile is the one you land on, so a misuse costs a
|
||||
// "the docs don't cover that" rather than an invented claim about the system.
|
||||
const PROFILES = {
|
||||
varaverk: { turns: 3, kind: true,
|
||||
hint: 'Answers only from Varaverk\'s own docs, with sources. Says so when they don\'t cover it.' },
|
||||
chat: { turns: 8, kind: false,
|
||||
hint: 'Ordinary conversation. Knows your memory notes, but not the docs — it\'ll point you back here for specifics.' },
|
||||
code: { turns: 4, kind: false,
|
||||
hint: 'Drafts short scripts for Custom Scripts. First drafts — it flags flags it isn\'t sure of. Test before trusting.' },
|
||||
};
|
||||
|
||||
let profile = 'varaverk';
|
||||
let history = []; // {role, content} — trimmed per profile
|
||||
let busy = false;
|
||||
let lastSources = [];
|
||||
|
||||
@@ -463,9 +492,10 @@ if (is_dir('/var/log/varaverk')) {
|
||||
try {
|
||||
const body = new URLSearchParams({
|
||||
action: 'ask',
|
||||
profile: profile,
|
||||
question: q,
|
||||
history: JSON.stringify(history.slice(-MAXTURN * 2)),
|
||||
kind: $('vv-ai-kind').value,
|
||||
history: JSON.stringify(history.slice(-PROFILES[profile].turns * 2)),
|
||||
kind: PROFILES[profile].kind ? $('vv-ai-kind').value : '',
|
||||
think: $('vv-ai-think').checked ? '1' : '0',
|
||||
});
|
||||
res = fetch(API, {
|
||||
@@ -516,7 +546,8 @@ if (is_dir('/var/log/varaverk')) {
|
||||
if (j.status === 'done') {
|
||||
addAnswer(j);
|
||||
history.push({ role: 'assistant', content: j.answer });
|
||||
if (history.length > MAXTURN * 2) history = history.slice(-MAXTURN * 2);
|
||||
const cap = PROFILES[profile].turns * 2;
|
||||
if (history.length > cap) history = history.slice(-cap);
|
||||
fetch(API, { method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: new URLSearchParams({ action: 'clear', token }) }).catch(() => {});
|
||||
@@ -547,6 +578,32 @@ if (is_dir('/var/log/varaverk')) {
|
||||
if (s && s.path) vvAiOpen(s.path);
|
||||
};
|
||||
|
||||
// ── Profiles ────────────────────────────────────────────────────────────
|
||||
// Switching clears the conversation history sent to the model but leaves the transcript on
|
||||
// screen. Carrying turns across a profile change would mean feeding cited, retrieval-grounded
|
||||
// answers into a mode that has no retrieval — the model would keep referring to sources it can
|
||||
// no longer see. The visible marker is so the transcript still reads honestly afterwards.
|
||||
function setProfile(p) {
|
||||
if (!PROFILES[p] || p === profile) return;
|
||||
profile = p;
|
||||
document.querySelectorAll('.vv-ai-prof').forEach(b =>
|
||||
b.classList.toggle('active', b.dataset.prof === p));
|
||||
$('vv-ai-prof-hint').textContent = PROFILES[p].hint;
|
||||
$('vv-ai-kind').style.display = PROFILES[p].kind ? '' : 'none';
|
||||
|
||||
if (history.length) {
|
||||
const label = document.querySelector('.vv-ai-prof[data-prof="' + p + '"]').textContent;
|
||||
chat().appendChild(el('<div class="vv-ai-switch">switched to ' + esc(label)
|
||||
+ ' — earlier turns are no longer carried</div>'));
|
||||
scroll();
|
||||
}
|
||||
history = [];
|
||||
$('vv-ai-input').focus();
|
||||
}
|
||||
document.querySelectorAll('.vv-ai-prof').forEach(b =>
|
||||
b.addEventListener('click', () => setProfile(b.dataset.prof)));
|
||||
$('vv-ai-prof-hint').textContent = PROFILES[profile].hint;
|
||||
|
||||
// ── Memory panel ────────────────────────────────────────────────────────
|
||||
// Live character count against the cap, because the budget is the whole point: this text is
|
||||
// prepended to every single turn and competes with retrieval for a 16k context.
|
||||
|
||||
Reference in New Issue
Block a user