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:
Gmer4Lfe
2026-08-03 17:36:24 -04:00
parent d0b3588f6c
commit 8d393a1e1c
3 changed files with 157 additions and 43 deletions
+20 -7
View File
@@ -89,7 +89,12 @@ header('Content-Type: application/json');
header('Cache-Control: no-store, no-cache');
require_once dirname(__DIR__) . '/include/ai.php';
const VV_AI_MAX_TURNS = 3; // user+assistant pairs retained; see OPERATIONAL MODEL
// History depth is per profile, and decided here rather than by the page. Varaverk Assistant
// spends ~2500 of its 16384 on retrieved passages, so it cannot afford deep history; the other
// two retrieve nothing and can carry a real conversation. Reasoning is not stored in history,
// so it does not compound.
const VV_AI_PROFILES = ['varaverk' => 3, 'chat' => 8, 'code' => 4];
const VV_AI_MAX_TURNS = 3; // fallback when a profile is not recognised
const VV_AI_MAX_QUESTION = 4000; // characters
const VV_AI_MAX_HIST_MSG = 4000; // characters per retained message
const VV_AI_JOB_TTL = 3600; // seconds before a job file is reaped
@@ -179,7 +184,14 @@ if ($action === 'ask') {
echo json_encode(['ok' => false, 'error' => 'question exceeds ' . VV_AI_MAX_QUESTION . ' characters']); exit;
}
$kind = trim($_POST['kind'] ?? '');
$profile = trim($_POST['profile'] ?? 'varaverk');
if (!isset(VV_AI_PROFILES[$profile])) {
echo json_encode(['ok' => false, 'error' => 'Unknown profile: ' . $profile]); exit;
}
$maxTurns = VV_AI_PROFILES[$profile] ?? VV_AI_MAX_TURNS;
// The retrieval filter only means anything to the profile that retrieves.
$kind = $profile === 'varaverk' ? trim($_POST['kind'] ?? '') : '';
if ($kind !== '' && !in_array($kind, VV_AI_KINDS, true)) {
echo json_encode(['ok' => false, 'error' => 'Unknown kind: ' . $kind]); exit;
}
@@ -196,8 +208,8 @@ if ($action === 'ask') {
$clean[] = ['role' => $role, 'content' => mb_substr($text, 0, VV_AI_MAX_HIST_MSG)];
}
}
if (count($clean) > VV_AI_MAX_TURNS * 2) {
$clean = array_slice($clean, -(VV_AI_MAX_TURNS * 2));
if (count($clean) > $maxTurns * 2) {
$clean = array_slice($clean, -($maxTurns * 2));
}
$dir = vv_ai_job_dir();
@@ -226,12 +238,13 @@ if ($action === 'ask') {
. escapeshellarg($question) . ' '
. escapeshellarg(json_encode($clean)) . ' '
. escapeshellarg($kind) . ' '
. escapeshellarg(($_POST['think'] ?? '1') === '1' ? '1' : '0')
. escapeshellarg(($_POST['think'] ?? '1') === '1' ? '1' : '0') . ' '
. escapeshellarg($profile)
. ' >/dev/null 2>&1 </dev/null &';
$out = []; $rc = 0;
exec($cmd, $out, $rc);
vv_ai_log(sprintf('ask token=%s rc=%d kind=%s q=%s',
substr($token, 0, 12), $rc, $kind ?: '-', mb_substr($question, 0, 80)));
vv_ai_log(sprintf('ask token=%s rc=%d profile=%s kind=%s q=%s',
substr($token, 0, 12), $rc, $profile, $kind ?: '-', mb_substr($question, 0, 80)));
echo json_encode(['ok' => true, 'token' => $token]);
exit;