AI Providers - the AI services ITFlow asks (functions/ai.php): a * ready-made kind each (OpenAI, Claude, Gemini, Azure, a model on your own * network...), its address, whether its key is saved (never shown), whether * passwords and keys are taken out of what is sent to it, the models on it and * what each is for, and its use in the last 30 days. Above, which model each * use case lands on. Test asks it for its models; Add and Edit are one window * (modals/ai/ai_provider_edit.php); the rest are admin/post/ai_provider.php. */ // Default Column Sortby Filter $sort = "ai_provider_name"; $order = "ASC"; require_once "includes/inc_all_admin.php"; // Only these sort - an unknown column would take the page down $sortable_columns = [ 'ai_provider_name' => "ai_provider_name $order", 'ai_provider_kind' => "ai_provider_kind $order, ai_provider_name ASC", 'model_count' => "model_count $order, ai_provider_name ASC", 'requests' => "requests $order, ai_provider_name ASC", 'last_used' => "last_used IS NULL, last_used $order, ai_provider_name ASC", ]; if (!isset($sortable_columns[$sort])) { $sort = 'ai_provider_name'; } $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS ai_provider_id, ai_provider_name, ai_provider_kind, ai_provider_api_url, ai_provider_redact, ai_provider_api_key IS NOT NULL AND ai_provider_api_key <> '' AS has_key, (SELECT COUNT(*) FROM ai_models WHERE ai_model_ai_provider_id = ai_provider_id) AS model_count, COALESCE(used.requests, 0) AS requests, COALESCE(used.failed, 0) AS failed, COALESCE(used.tokens_in, 0) AS tokens_in, COALESCE(used.tokens_out, 0) AS tokens_out, used.last_used FROM ai_providers LEFT JOIN ( SELECT ai_usage_provider_id, SUM(ai_usage_created_at >= NOW() - INTERVAL 30 DAY) AS requests, SUM(ai_usage_created_at >= NOW() - INTERVAL 30 DAY AND ai_usage_ok = 0) AS failed, SUM(IF(ai_usage_created_at >= NOW() - INTERVAL 30 DAY, ai_usage_tokens_in, 0)) AS tokens_in, SUM(IF(ai_usage_created_at >= NOW() - INTERVAL 30 DAY, ai_usage_tokens_out, 0)) AS tokens_out, MAX(ai_usage_created_at) AS last_used FROM ai_usage WHERE ai_usage_feature <> 'test' GROUP BY ai_usage_provider_id ) used ON used.ai_usage_provider_id = ai_provider_id WHERE (ai_provider_name LIKE '%$q%' OR ai_provider_kind LIKE '%$q%' OR ai_provider_api_url LIKE '%$q%' OR EXISTS (SELECT 1 FROM ai_models WHERE ai_model_ai_provider_id = ai_provider_id AND ai_model_name LIKE '%$q%')) ORDER BY {$sortable_columns[$sort]}, ai_provider_id ASC LIMIT $record_from, $record_to"); $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); // Each provider's models, with what they are for - and those nothing uses $models_by_provider = []; $model_sql = mysqli_query($mysqli, "SELECT ai_model_id, ai_model_name, ai_model_use_case, ai_model_ai_provider_id FROM ai_models ORDER BY FIELD(ai_model_use_case, 'General', 'Tickets', 'Documentation', 'Billing'), ai_model_name"); while ($model = mysqli_fetch_assoc($model_sql)) { $models_by_provider[intval($model['ai_model_ai_provider_id'])][] = $model; } $unused = aiUseCaseMap()['unused']; $any_provider = intval(mysqli_fetch_row(mysqli_query($mysqli, "SELECT COUNT(*) FROM ai_providers"))[0]) > 0; $sort_link = function ($column, $label) use ($url_query_strings_sort, $sort, $disp, $order_icon) { return '' . $label . ($sort === $column ? " $order_icon" : '') . ''; }; $add_url = 'modals/ai/ai_provider_edit.php'; $csrf = $_SESSION['csrf_token']; ?>

AI Providers

Models Usage
"> $provider_id]); $model_names = array_column($models, 'ai_model_name'); $delete_text = ($models ? (count($models) === 1 ? "Its model, {$model_names[0]}, goes with it." : 'Its ' . count($models) . ' models go with it - ' . implode(', ', $model_names) . '.') : 'It has no models.') . ($stopping ? ' AI stops for ' . implode(' and ', $stopping) . ' until another model is set up.' : ''); ?>
Key
Your network Secrets sent
Saved Needs one - add it in Edit None
· Not used
Add a model
in · out
Last
'AI providers', 'icon' => 'fa-robot', 'text' => 'Connect OpenAI, Claude, Gemini, Azure OpenAI, or a model on your own network like Ollama - then agents can reword replies, summarize tickets and draft documents, with passwords and keys taken out of what is sent.', 'actions' => [['label' => 'Add Provider', 'modal' => $add_url]], ]; ?>