AI Models (admin/ai_models.php): adding one, changing it, * trying it and deleting it. * * The window asks first (check_ai_model, JSON) and says what is wrong beside * each field; with 'try' it also sends the model a one-line request, as it is * in the window - unsaved - and says what came back, how long it took and the * tokens it used (recorded in AI Usage as a test). What changed is in the * audit log, from what to what - the instructions only as changed. */ defined('FROM_POST_HANDLER') || die("Direct file access is not allowed"); if (isset($_POST['check_ai_model']) || isset($_POST['add_ai_model']) || isset($_POST['edit_ai_model'])) { validateCSRFToken(); $checking = isset($_POST['check_ai_model']); $model_id = intval($_POST['model_id'] ?? 0); $before = $model_id ? aiModelRow($model_id) : null; $answer = function ($ok, $errors, $extra = []) { header('Content-Type: application/json; charset=utf-8'); echo json_encode(['ok' => $ok, 'errors' => (object) $errors] + $extra); exit; }; if ($model_id && !$before) { if ($checking) { $answer(false, ['model' => 'That model no longer exists - close this and look again.']); } flashAlert('That AI model no longer exists.', 'error'); redirect('ai_models.php'); } [$after, $errors, $provider] = aiModelCheck($_POST); if ($checking) { $tried = []; if (!$errors && !empty($_POST['try'])) { // As it is in the window, on its provider $model = $provider + [ 'ai_model_id' => $model_id, 'ai_model_name' => $after['ai_model_name'], 'ai_model_use_case' => $after['ai_model_use_case'], 'ai_model_temperature' => $after['ai_model_temperature'], 'ai_model_prompt' => $after['ai_model_prompt'], ]; $started = microtime(true); $result = callAiApi($model, [ ['role' => 'system', 'content' => 'You are checking that you are set up and working. Answer in one short, friendly sentence.'], ['role' => 'user', 'content' => 'Say hello, and which model you are.'], ], ['feature' => 'test']); $ms = intval(round((microtime(true) - $started) * 1000)); if ($result['ok']) { $tried = ['reply' => mb_substr(trim(strip_tags($result['content'])), 0, 400), 'ms' => $ms]; } else { $errors['model'] = $result['error']; } } $answer(!$errors, $errors, $tried); } // Only reached without the window's script - say the first thing wrong if ($errors) { flashAlert('Not saved - ' . escapeHtml(reset($errors)), 'error'); redirect(); } $name = $after['ai_model_name']; $use_case = $after['ai_model_use_case']; if (!$before) { // A second one for a use case isn't used - the first one is - so it is said $already = getAiModel($use_case); $model_id = aiModelWrite($after); logAudit("AI Model", "Create", "$session_name added AI model " . escapeSql("$name on {$provider['ai_provider_name']}, for $use_case"), 0, $model_id); if ($already && $already['ai_model_use_case'] === $use_case) { flashAlert('' . escapeHtml($name) . ' added - but ' . escapeHtml($already['ai_model_name']) . " is for $use_case already and is the one used. Delete it, or set it to something else, to use this one", 'warning'); } else { flashAlert('' . escapeHtml($name) . " added - it is used for " . ($use_case === 'General' ? 'everything without a model of its own' : $use_case)); } redirect('ai_models.php'); } $lines = aiModelChangeLines($before, $after); if (!$lines) { flashAlert('Nothing changed on ' . escapeHtml($name) . '', 'info'); redirect(); } aiModelWrite($after, $model_id); logAudit("AI Model", "Edit", "$session_name changed AI model " . escapeSql("$name: " . implode('; ', $lines)), 0, $model_id); flashAlert('' . escapeHtml($name) . ' saved - ' . escapeHtml(count($lines) === 1 ? $lines[0] : count($lines) . ' changes')); redirect(); } // Delete a model - what it was for falls back on General, or stops when it was General if (isset($_GET['delete_ai_model'])) { validateCSRFToken(); $row = aiModelRow($_GET['delete_ai_model']); if (!$row) { flashAlert('That AI model no longer exists.', 'error'); redirect('ai_models.php'); } $model_id = intval($row['ai_model_id']); $name = $row['ai_model_name']; mysqli_query($mysqli, "DELETE FROM ai_models WHERE ai_model_id = $model_id"); logAudit("AI Model", "Delete", "$session_name deleted AI model " . escapeSql("$name on {$row['ai_provider_name']}, for {$row['ai_model_use_case']}"), 0, $model_id); flashAlert('' . escapeHtml($name) . ' deleted', 'error'); redirect('ai_models.php'); }