Give the AI subsystem one profile table and one collection, read everywhere
This commit is contained in:
@@ -82,48 +82,10 @@ require_once __DIR__ . '/config.php';
|
||||
define('VV_AI_JOB_DIR', '/tmp/varaverk_ai_jobs');
|
||||
const VV_AI_KINDS = ['header', 'readme', 'manual', 'template', 'doc'];
|
||||
|
||||
// ── What each profile is allowed to see and do ───────────────────────────────────────────────
|
||||
// A profile is a contract plus a set of inputs, and the inputs are the half that has to be
|
||||
// enforced rather than requested. This table is that half, in one place.
|
||||
//
|
||||
// It exists because the alternative already failed. The same permissions used to live as a dozen
|
||||
// `$profile === 'varaverk' || $profile === 'troubleshoot'` conditions spread across the worker,
|
||||
// and answering "may chat ever be shown a log?" meant reading all of them. It could — a gate
|
||||
// added for run-outcome questions granted it by omission, and the chat profile, whose entire
|
||||
// value is that it has NOT been shown this installation, was one phrasing away from being handed
|
||||
// a health sweep and 120 lines of log. Nothing about that was visible at the point of the
|
||||
// mistake. Here it would have been one missing word on one line.
|
||||
//
|
||||
// A capability is permission, not need. varaverk holds 'health' but only attaches it when the
|
||||
// question looks diagnostic; troubleshoot attaches it always. The gates decide whether an input
|
||||
// is warranted, this decides whether it is allowed, and a gate can never widen the grant.
|
||||
//
|
||||
// The ordering is deliberate: chat holds nothing, and that emptiness is a guarantee, not an
|
||||
// oversight. Anything added to it stops being general chat and becomes an assistant that
|
||||
// sometimes lies about this installation.
|
||||
const VV_AI_CAPS = [
|
||||
// retrieval passages from the index, and the kind filter the page exposes for them
|
||||
'retrieve' => ['varaverk', 'troubleshoot'],
|
||||
'kind_filter' => ['varaverk'],
|
||||
// live health sweep measured at question time
|
||||
'health' => ['varaverk', 'troubleshoot'],
|
||||
// run record + log tail for a script named in the question
|
||||
'run_evidence' => ['varaverk', 'troubleshoot'],
|
||||
// log tail for whatever the operator currently has open
|
||||
'scoped_log' => ['troubleshoot'],
|
||||
// operator-written history of what previously went wrong with this thing
|
||||
'incidents' => ['varaverk', 'troubleshoot'],
|
||||
// deterministic "where does this conf key actually live" lookup
|
||||
'conf_lookup' => ['varaverk', 'troubleshoot'],
|
||||
// may file a bug report against Varaverk itself
|
||||
'file_bugs' => ['troubleshoot'],
|
||||
// destructive-operation scan of generated shell
|
||||
'code_scan' => ['code'],
|
||||
];
|
||||
|
||||
function vv_ai_profile_can(string $profile, string $cap): bool {
|
||||
return in_array($profile, VV_AI_CAPS[$cap] ?? [], true);
|
||||
}
|
||||
// Profiles — what each one is, and what it is allowed to see and do. One table, in one file,
|
||||
// read by everything: this endpoint, the worker, the shared chat include and the Scheduler dock.
|
||||
// vv_ai_profile_can() and friends come from there.
|
||||
require_once __DIR__ . '/ai_profiles.php';
|
||||
|
||||
// Whether a General Chat message is really about this installation. Shared by the deterministic
|
||||
// backstop and the handoff, so both agree by construction: a question the backstop would have
|
||||
@@ -472,6 +434,43 @@ function vv_ai_stats(): array {
|
||||
];
|
||||
}
|
||||
|
||||
// ── Shared collection ─────────────────────────────────────────────────────────
|
||||
// vv_ai_stats() costs about a second on this host — vv_ai_runtime_stats() alone is 60-480ms
|
||||
// depending on how quickly Ollama and nvidia-smi answer, and it was being paid by the AI tab
|
||||
// every 30 seconds per open tab, plus again by anything else that wanted the same numbers.
|
||||
//
|
||||
// So it is collected once, by Tools/api_cache_writer.php, into the 'ai' cache; every surface
|
||||
// reads that. This is the same arrangement the monitor and arrs payloads already use and for the
|
||||
// same reason — polling faster cannot make the figures newer, it only decides how soon a page
|
||||
// notices the writer's update.
|
||||
//
|
||||
// ?live=1 stays available for the one case that needs it: you changed something and want to see
|
||||
// the result rather than a payload written before you changed it.
|
||||
function vv_ai_stats_cached(bool $live = false): array {
|
||||
if (!$live) {
|
||||
$c = vv_cache_read('ai', 300);
|
||||
if ($c !== null) return $c;
|
||||
}
|
||||
return vv_ai_stats();
|
||||
}
|
||||
|
||||
// The Monitor tab's slice of the same collection. Derived rather than collected: taking the AI
|
||||
// row's figures from a second call to vv_ai_runtime_stats() would pay the whole cost twice per
|
||||
// cache write, and — worse — the dashboard and the AI tab could disagree about whether the model
|
||||
// is resident, because they would have asked at different moments.
|
||||
//
|
||||
// Tokens are not part of vv_ai_stats(): that function's shape is the AI tab's banner contract,
|
||||
// and the ledger read is 15ms, so it is fetched here rather than widening the payload everything
|
||||
// else carries.
|
||||
function vv_ai_monitor_block(array $stats): array {
|
||||
return [
|
||||
'model' => $stats['model'] ?? '',
|
||||
'runtime' => $stats['runtime'] ?? [],
|
||||
'index' => $stats['index'] ?? [],
|
||||
'tokens' => vv_ai_token_stats()['today'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
// Retrieval via AI/lib/cli.js. Returns ['ok'=>bool,'results'=>[],'intents'=>[],'error'=>?string].
|
||||
function vv_ai_retrieve(string $query, string $kind = '', string $section = '', ?int $k = null): array {
|
||||
$cfg = vv_ai_config();
|
||||
|
||||
Reference in New Issue
Block a user