Bill a turn to the node that asked, not the node that generated

Every turn runs on the AI owner now, so the ledger's per-host column had
collapsed to a single name and the fleet breakdown said nothing. The asking
node travels with the request and is validated before it is written, which
also retires ai_token_sync.sh — a partner ledger is empty by construction.
This commit is contained in:
Gmer4Lfe
2026-08-20 20:04:08 -04:00
parent 0621f66889
commit 111c70d6b4
4 changed files with 46 additions and 15 deletions
+13 -3
View File
@@ -122,8 +122,17 @@ if ($explain) {
exit(2);
}
} else {
[$jobFile, $question, $historyJson, $kind, $think, $profile, $scope, $webArg] =
array_slice($argv, 1, 8) + array_fill(0, 8, '');
// Ninth argument is the node that asked, which is not necessarily the node generating: the
// mesh shares one AI, so a turn started on a mirror runs here. Without it every row in the
// ledger reads as the owner's and the tab can no longer say who spent what.
//
// Optional, and validated rather than trusted. It arrives from the RPC layer's own view of
// the caller, but this is a command line and an unrecognisable value must fall back to "this
// node" rather than write a slot name nobody can place.
[$jobFile, $question, $historyJson, $kind, $think, $profile, $scope, $webArg, $askNode] =
array_slice($argv, 1, 9) + array_fill(0, 9, '');
if (!preg_match('/^host\d+$/', (string)$askNode)) $askNode = '';
if ($jobFile === '' || $question === '') exit(1);
if (!preg_match('#/[0-9a-f]{32}\.json$#', $jobFile)) exit(1);
@@ -1239,7 +1248,8 @@ $tokS = $evalNs > 0 ? round($evalCount / ($evalNs / 1e9), 1) : null;
// Accounting before the job file is written, so a turn is counted even if the tab has already
// been closed and nobody ever reads the result. Best-effort by contract — it cannot throw.
vv_ai_token_record($profile, 'webgui', (int)($d['prompt_eval_count'] ?? 0), $evalCount, $tokS);
vv_ai_token_record($profile, 'webgui', (int)($d['prompt_eval_count'] ?? 0), $evalCount, $tokS,
$askNode ?? '');
// profile is the one that actually answered, not the one that was asked for — they differ on a
// handoff. Reported so the page can show which contract produced the answer rather than the