89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
// Generated, do not edit. sha256:9505c124878c8b5af0571c1b4757d6fc332a4d6d342508d9c8ff717ce279c7db
|
|||
|
|
import * as db from '../db.js';
|
||
|
|
import { CONFIG, ENUMS } from '../../shared/config.js';
|
||
|
|
import { assertEnum, nowIso } from './_shared.js';
|
||
|
|
import { emitChange } from './_events.js';
|
||
|
|
import { limitText } from '../ai/limits.js';
|
||
|
|
|
||
|
|
function uitRij(r) {
|
||
|
|
if (!r) return r;
|
||
|
|
let velden = null;
|
||
|
|
if (r.fieldsShared != null) {
|
||
|
|
try { velden = JSON.parse(r.fieldsShared); } catch { velden = []; }
|
||
|
|
if (!Array.isArray(velden)) velden = [];
|
||
|
|
}
|
||
|
|
return { ...r, fieldsShared: velden };
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function logAiToolCall(entry) {
|
||
|
|
assertEnum('outcome', ENUMS.AI_CALL_OUTCOME, entry.outcome);
|
||
|
|
if (!entry.sessionId) throw new Error('An audit entry without a session cannot be traced.');
|
||
|
|
if (!entry.tool) throw new Error('An audit entry without a tool name cannot be traced.');
|
||
|
|
const momenten = entry.messages == null ? [] : [...entry.messages];
|
||
|
|
for (const m of momenten) assertEnum('kind', ENUMS.AI_MESSAGE_KIND, m?.kind);
|
||
|
|
const velden = entry.fieldsShared == null ? null : JSON.stringify([...entry.fieldsShared]);
|
||
|
|
db.run(`INSERT INTO AiToolCall
|
||
|
|
(sessionId, client, tool, arguments, fieldsShared, charactersOut, outcome, detail, occurredAt, serverVersion)
|
||
|
|
VALUES (?,?,?,?,?,?,?,?,?,?)`,
|
||
|
|
[entry.sessionId, entry.client ?? null, entry.tool,
|
||
|
|
entry.arguments != null ? JSON.stringify(entry.arguments) : null,
|
||
|
|
velden, entry.charactersOut ?? null, entry.outcome, entry.detail ?? null,
|
||
|
|
entry.occurredAt ?? nowIso(), entry.serverVersion ?? null]);
|
||
|
|
const id = db.lastId();
|
||
|
|
for (const m of momenten) {
|
||
|
|
const { text } = limitText(String(m.text ?? ''), CONFIG.AI.LIMITS.MAX_LOG_CHARS);
|
||
|
|
db.run(`INSERT INTO AiToolCallMessage (callId, kind, text, occurredAt) VALUES (?,?,?,?)`,
|
||
|
|
[id, m.kind, text, m.occurredAt ?? nowIso()]);
|
||
|
|
}
|
||
|
|
await db.persist();
|
||
|
|
emitChange('ai-activity-changed');
|
||
|
|
return id;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function countAiToolCallsSince(sinds) {
|
||
|
|
const vanaf = Number.isInteger(sinds) && sinds >= 0 ? sinds : 0;
|
||
|
|
const r = db.one(`SELECT COUNT(*) AS n, MAX(id) AS laatste FROM AiToolCall WHERE id > ?`, [vanaf]);
|
||
|
|
const alles = db.one(`SELECT MAX(id) AS laatste FROM AiToolCall`);
|
||
|
|
return {
|
||
|
|
count: r ? r.n : 0,
|
||
|
|
newestId: (alles && alles.laatste != null) ? alles.laatste : 0,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function aiToolCallMessages(callId) {
|
||
|
|
const n = Number(callId);
|
||
|
|
if (!Number.isInteger(n) || n <= 0) return [];
|
||
|
|
return db.all(`SELECT id, callId, kind, text, occurredAt FROM AiToolCallMessage
|
||
|
|
WHERE callId = ? ORDER BY id`, [n]);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function aiSessions(uren = 24) {
|
||
|
|
const grens = new Date(Date.now() - Math.max(1, Number(uren) || 24) * 3600_000).toISOString();
|
||
|
|
return db.all(
|
||
|
|
`SELECT sessionId, client, serverVersion,
|
||
|
|
COUNT(*) AS calls, MIN(occurredAt) AS firstAt, MAX(occurredAt) AS lastAt
|
||
|
|
FROM AiToolCall
|
||
|
|
WHERE occurredAt >= ?
|
||
|
|
GROUP BY sessionId, client, serverVersion
|
||
|
|
ORDER BY lastAt DESC`, [grens])
|
||
|
|
.map(r => ({ ...r, zonderVersie: r.serverVersion == null }));
|
||
|
|
}
|
||
|
|
|
||
|
|
export function listAiToolCalls(limit = CONFIG.AI.AUDIT_PAGE_SIZE) {
|
||
|
|
const n = Math.min(Math.max(Number(limit) || CONFIG.AI.AUDIT_PAGE_SIZE, 1), 1000);
|
||
|
|
return db.all(`SELECT * FROM AiToolCall ORDER BY occurredAt DESC, id DESC LIMIT ?`, [n]).map(uitRij);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function countAiToolCalls() {
|
||
|
|
const totaal = db.one(`SELECT COUNT(*) AS n FROM AiToolCall`);
|
||
|
|
const perUitkomst = db.all(`SELECT outcome, COUNT(*) AS n FROM AiToolCall GROUP BY outcome`);
|
||
|
|
const uitkomsten = {};
|
||
|
|
for (const k of ENUMS.AI_CALL_OUTCOME) uitkomsten[k] = 0;
|
||
|
|
for (const r of perUitkomst) uitkomsten[r.outcome] = r.n;
|
||
|
|
return { total: totaal ? totaal.n : 0, byOutcome: uitkomsten };
|
||
|
|
}
|
||
|
|
|
||
|
|
export function lastAiToolCallAt() {
|
||
|
|
const r = db.one(`SELECT occurredAt FROM AiToolCall ORDER BY occurredAt DESC, id DESC LIMIT 1`);
|
||
|
|
return r ? r.occurredAt : null;
|
||
|
|
}
|