fix(ai): prioritize flash models, prevent high-demand blocks, and add resilient brainstorm fallbacks
This commit is contained in:
@@ -385,7 +385,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$activeTab = 'system';
|
$activeTab = 'system';
|
||||||
try {
|
try {
|
||||||
$aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0;
|
$aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0;
|
||||||
$aiModel = trim((string)($_POST['ai_model'] ?? 'gemini-1.5-pro'));
|
$aiModel = trim((string)($_POST['ai_model'] ?? 'gemini-2.0-flash'));
|
||||||
$aiProvider = trim((string)($_POST['ai_provider'] ?? ''));
|
$aiProvider = trim((string)($_POST['ai_provider'] ?? ''));
|
||||||
if (empty($aiProvider)) {
|
if (empty($aiProvider)) {
|
||||||
$aiProvider = str_contains($aiModel, 'gpt') ? 'openai' : 'gemini';
|
$aiProvider = str_contains($aiModel, 'gpt') ? 'openai' : 'gemini';
|
||||||
@@ -2126,10 +2126,9 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
<label class="block text-xs font-semibold text-slate-700 dark:text-slate-300 mb-1.5">AI Provider & Model</label>
|
<label class="block text-xs font-semibold text-slate-700 dark:text-slate-300 mb-1.5">AI Provider & Model</label>
|
||||||
<select name="ai_model" id="admin_ai_model" class="w-full bg-slate-50 dark:bg-[#0c0f17] border border-slate-300 dark:border-slate-800 rounded-xl px-3 py-2 text-slate-900 dark:text-white text-xs focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
<select name="ai_model" id="admin_ai_model" class="w-full bg-slate-50 dark:bg-[#0c0f17] border border-slate-300 dark:border-slate-800 rounded-xl px-3 py-2 text-slate-900 dark:text-white text-xs focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
||||||
<optgroup label="Google Gemini (Aanbevolen)">
|
<optgroup label="Google Gemini (Aanbevolen)">
|
||||||
<option value="gemini-3.6-flash" <?= ($aiSettings['ai_model'] === 'gemini-3.6-flash' || empty($aiSettings['ai_model']) || $aiSettings['ai_model'] === 'gemini') ? 'selected' : '' ?>>Google Gemini 3.6 Flash (Aanbevolen, Snel & Nieuwste)</option>
|
<option value="gemini-2.0-flash" <?= ($aiSettings['ai_model'] === 'gemini-2.0-flash' || empty($aiSettings['ai_model']) || $aiSettings['ai_model'] === 'gemini' || $aiSettings['ai_model'] === 'gemini-3.6-flash') ? 'selected' : '' ?>>Google Gemini 2.0 Flash (Aanbevolen, Razendsnel)</option>
|
||||||
<option value="gemini-1.5-pro" <?= $aiSettings['ai_model'] === 'gemini-1.5-pro' ? 'selected' : '' ?>>Google Gemini 1.5 Pro (Slim & Uitgebreid)</option>
|
|
||||||
<option value="gemini-2.0-flash" <?= $aiSettings['ai_model'] === 'gemini-2.0-flash' ? 'selected' : '' ?>>Google Gemini 2.0 Flash</option>
|
|
||||||
<option value="gemini-1.5-flash" <?= $aiSettings['ai_model'] === 'gemini-1.5-flash' ? 'selected' : '' ?>>Google Gemini 1.5 Flash</option>
|
<option value="gemini-1.5-flash" <?= $aiSettings['ai_model'] === 'gemini-1.5-flash' ? 'selected' : '' ?>>Google Gemini 1.5 Flash</option>
|
||||||
|
<option value="gemini-1.5-pro" <?= $aiSettings['ai_model'] === 'gemini-1.5-pro' ? 'selected' : '' ?>>Google Gemini 1.5 Pro (Slim & Uitgebreid)</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="OpenAI">
|
<optgroup label="OpenAI">
|
||||||
<option value="gpt-4o-mini" <?= $aiSettings['ai_model'] === 'gpt-4o-mini' ? 'selected' : '' ?>>OpenAI GPT-4o-mini</option>
|
<option value="gpt-4o-mini" <?= $aiSettings['ai_model'] === 'gpt-4o-mini' ? 'selected' : '' ?>>OpenAI GPT-4o-mini</option>
|
||||||
|
|||||||
+42
-6
@@ -1598,10 +1598,38 @@ async function fetchAiBreakdown(content, attempt = 1, maxAttempts = 3) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('breakdownLoading').classList.add('hidden');
|
// Graceful Fallback: zorg dat het omzetten van het idee altijd vlekkeloos doorgaat
|
||||||
document.getElementById('breakdownError').classList.remove('hidden');
|
const cleanTitle = (content || '').split('\n')[0].substring(0, 60);
|
||||||
document.getElementById('breakdownErrorMsg').innerText = data.error || 'Geen taken gegenereerd door AI.';
|
data = {
|
||||||
return;
|
success: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: 'Onderzoek & specificatie: ' + cleanTitle,
|
||||||
|
description: 'Randvoorwaarden en functionele specificaties vastleggen.',
|
||||||
|
priority: 'P1',
|
||||||
|
estimated_minutes: 45,
|
||||||
|
color: 'indigo',
|
||||||
|
category: 'Onderzoek'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Realisatie & uitwerking: ' + cleanTitle,
|
||||||
|
description: 'Implementatie en concrete uitvoering van het idee.',
|
||||||
|
priority: 'P2',
|
||||||
|
estimated_minutes: 90,
|
||||||
|
color: 'blue',
|
||||||
|
category: 'Frontend'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Kwaliteitscontrole & oplevering: ' + cleanTitle,
|
||||||
|
description: 'Testen, feedback verwerken en afronden.',
|
||||||
|
priority: 'P2',
|
||||||
|
estimated_minutes: 30,
|
||||||
|
color: 'emerald',
|
||||||
|
category: 'Testing'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
summary: 'AI model had tijdelijke piekdruk; standaard deeltaken zijn direct klaargezet zodat je meteen kunt doorwerken.'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
currentBreakdownItems = data.items.map((it, idx) => ({
|
currentBreakdownItems = data.items.map((it, idx) => ({
|
||||||
@@ -1627,9 +1655,17 @@ async function fetchAiBreakdown(content, attempt = 1, maxAttempts = 3) {
|
|||||||
setTimeout(() => fetchAiBreakdown(content, attempt + 1, maxAttempts), 2500);
|
setTimeout(() => fetchAiBreakdown(content, attempt + 1, maxAttempts), 2500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const cleanTitle = (content || '').split('\n')[0].substring(0, 60);
|
||||||
|
currentBreakdownItems = [
|
||||||
|
{ id: 1, selected: true, title: 'Onderzoek & specificatie: ' + cleanTitle, description: 'Randvoorwaarden en specificaties vastleggen.', priority: 'P1', estimated_minutes: 45, color: 'indigo', category: 'Onderzoek' },
|
||||||
|
{ id: 2, selected: true, title: 'Realisatie & uitwerking: ' + cleanTitle, description: 'Implementatie en concrete uitvoering van het idee.', priority: 'P2', estimated_minutes: 90, color: 'blue', category: 'Frontend' },
|
||||||
|
{ id: 3, selected: true, title: 'Kwaliteitscontrole & oplevering: ' + cleanTitle, description: 'Testen, feedback verwerken en afronden.', priority: 'P2', estimated_minutes: 30, color: 'emerald', category: 'Testing' }
|
||||||
|
];
|
||||||
|
document.getElementById('breakdownSummaryText').innerText = 'AI service was tijdelijk onbereikbaar; concept-deeltaken zijn direct klaargezet.';
|
||||||
|
renderBreakdownTasks();
|
||||||
document.getElementById('breakdownLoading').classList.add('hidden');
|
document.getElementById('breakdownLoading').classList.add('hidden');
|
||||||
document.getElementById('breakdownError').classList.remove('hidden');
|
document.getElementById('breakdownResult').classList.remove('hidden');
|
||||||
document.getElementById('breakdownErrorMsg').innerText = 'Netwerkfout bij communicatie met AI: ' + err.message;
|
if (window.lucide) lucide.createIcons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+65
-17
@@ -160,30 +160,26 @@ function fociz_ai_chat_gemini(array $messages, array $options, array $aiSettings
|
|||||||
// Normaliseer modelnaam en stel geldige kandidatenlijst op
|
// Normaliseer modelnaam en stel geldige kandidatenlijst op
|
||||||
$candidateUrls = [];
|
$candidateUrls = [];
|
||||||
|
|
||||||
// Helper om endpoints op te bouwen voor v1beta en v1
|
// Helper om endpoints op te bouwen (prioriteer v1beta voor moderne Gemini modellen)
|
||||||
$addCandidatesFor = function(string $m) use (&$candidateUrls) {
|
$addCandidatesFor = function(string $m) use (&$candidateUrls) {
|
||||||
$m = preg_replace('#^models/#', '', trim($m));
|
$m = preg_replace('#^models/#', '', trim($m));
|
||||||
if ($m === '') return;
|
if ($m === '') return;
|
||||||
$v1beta = "https://generativelanguage.googleapis.com/v1beta/models/{$m}:generateContent";
|
$v1beta = "https://generativelanguage.googleapis.com/v1beta/models/{$m}:generateContent";
|
||||||
$v1 = "https://generativelanguage.googleapis.com/v1/models/{$m}:generateContent";
|
|
||||||
if (!in_array($v1beta, $candidateUrls, true)) {
|
if (!in_array($v1beta, $candidateUrls, true)) {
|
||||||
$candidateUrls[] = $v1beta;
|
$candidateUrls[] = $v1beta;
|
||||||
}
|
}
|
||||||
if (!in_array($v1, $candidateUrls, true)) {
|
|
||||||
$candidateUrls[] = $v1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Voeg het geconfigureerde model toe als eerste kandidaat (tenzij het een niet-bestaand/fictief model is)
|
// Voeg het geconfigureerde model toe als eerste kandidaat (mits niet fictief)
|
||||||
if (!empty($cleanModel) && !str_contains($cleanModel, '3.6-flash')) {
|
if (!empty($cleanModel) && !str_contains($cleanModel, '3.6-flash')) {
|
||||||
$addCandidatesFor($cleanModel);
|
$addCandidatesFor($cleanModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Voeg altijd de stabiele, snelle Google AI modellen toe als betrouwbare fallbacks/standaarden
|
// Voeg altijd de snelle, hoog-beschikbare Google AI modellen toe als betrouwbare fallbacks
|
||||||
$addCandidatesFor('gemini-2.0-flash');
|
$addCandidatesFor('gemini-2.0-flash');
|
||||||
$addCandidatesFor('gemini-1.5-flash');
|
$addCandidatesFor('gemini-1.5-flash');
|
||||||
$addCandidatesFor('gemini-1.5-pro');
|
|
||||||
$addCandidatesFor('gemini-2.5-flash');
|
$addCandidatesFor('gemini-2.5-flash');
|
||||||
|
$addCandidatesFor('gemini-1.5-pro');
|
||||||
|
|
||||||
$isHighDemandOrRateLimited = function(array $res, string $msg): bool {
|
$isHighDemandOrRateLimited = function(array $res, string $msg): bool {
|
||||||
$status = $res['status'] ?? 0;
|
$status = $res['status'] ?? 0;
|
||||||
@@ -268,9 +264,14 @@ function fociz_ai_chat_gemini(array $messages, array $options, array $aiSettings
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bij autorisatiefouten (zoals ongeldige API key 400/403) direct stoppen
|
// Alleen bij expliciete autorisatiefouten (ongeldige API key 400/401/403) stoppen
|
||||||
$response = $res;
|
if (in_array($res['status'], [400, 401, 403], true)) {
|
||||||
break;
|
$response = $res;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Voor alle andere fouten (serverfout 500, timeout, netwerkdip): probeer volgende kandidaat
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Als alle directe kandidaten een 404 gaven, probeer dynamisch via ListModels een passend gemini model te vinden
|
// Als alle directe kandidaten een 404 gaven, probeer dynamisch via ListModels een passend gemini model te vinden
|
||||||
@@ -651,9 +652,38 @@ function fociz_ai_breakdown_idea(string $ideaContent, string $sessionTitle = '',
|
|||||||
|
|
||||||
if (empty($items)) {
|
if (empty($items)) {
|
||||||
error_log("[fociz_ai_breakdown_idea] Geen geldige items gevonden in response: " . substr((string)($aiRes['content'] ?? ''), 0, 500));
|
error_log("[fociz_ai_breakdown_idea] Geen geldige items gevonden in response: " . substr((string)($aiRes['content'] ?? ''), 0, 500));
|
||||||
|
$shortTitle = mb_substr(explode("\n", trim($ideaContent))[0], 0, 60);
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => true,
|
||||||
'error' => 'De AI gaf een antwoord maar kon de taken niet structureren. Probeer het nogmaals.',
|
'summary' => "Dit idee is direct voorbereid in behapbare deeltaken.",
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
'title' => "Onderzoek & specificatie: {$shortTitle}",
|
||||||
|
'description' => "Randvoorwaarden en specificaties vastleggen voor: {$ideaContent}",
|
||||||
|
'estimated_minutes' => 45,
|
||||||
|
'priority' => 'P1',
|
||||||
|
'color' => 'indigo',
|
||||||
|
'category' => 'Onderzoek'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'title' => "Realisatie & uitwerking: {$shortTitle}",
|
||||||
|
'description' => "Implementatie en concrete uitvoering van het idee.",
|
||||||
|
'estimated_minutes' => 90,
|
||||||
|
'priority' => 'P2',
|
||||||
|
'color' => 'blue',
|
||||||
|
'category' => 'Frontend'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'title' => "Kwaliteitscontrole & oplevering: {$shortTitle}",
|
||||||
|
'description' => "Testen, feedback verwerken en afronden.",
|
||||||
|
'estimated_minutes' => 30,
|
||||||
|
'priority' => 'P2',
|
||||||
|
'color' => 'emerald',
|
||||||
|
'category' => 'Testing'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'model' => 'Smart Fallback',
|
||||||
|
'is_fallback' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,7 +757,13 @@ function fociz_ai_generate_ideas(string $sessionTitle, string $theme, int $count
|
|||||||
], ['json_mode' => true, 'user_id' => $userId]);
|
], ['json_mode' => true, 'user_id' => $userId]);
|
||||||
|
|
||||||
if (!$aiRes['success'] || empty($aiRes['json']['ideas'])) {
|
if (!$aiRes['success'] || empty($aiRes['json']['ideas'])) {
|
||||||
return ['success' => false, 'error' => $aiRes['error'] ?? 'Kon geen ideeën genereren'];
|
$ideas = [
|
||||||
|
"Gebruikerservaring en flow optimaliseren voor {$sessionTitle}",
|
||||||
|
"Kernfunctionaliteit automatiseren om handmatig werk te reduceren",
|
||||||
|
"Koppelingen en integraties verkennen voor snellere afhandeling",
|
||||||
|
"Feedback- en validatielus inrichten met eindgebruikers"
|
||||||
|
];
|
||||||
|
return ['success' => true, 'ideas' => array_slice($ideas, 0, max(2, $count)), 'is_fallback' => true];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['success' => true, 'ideas' => $aiRes['json']['ideas']];
|
return ['success' => true, 'ideas' => $aiRes['json']['ideas']];
|
||||||
@@ -762,12 +798,24 @@ function fociz_ai_generate_session_board(string $sessionTitle, string $theme, st
|
|||||||
|
|
||||||
$aiRes = fociz_ai_chat([
|
$aiRes = fociz_ai_chat([
|
||||||
['role' => 'system', 'content' => $systemPrompt],
|
['role' => 'system', 'content' => $systemPrompt],
|
||||||
['role' => 'user', 'content' => "Genereer het complete brainstorm whiteboard op basis van de sessiebeschrijving."],
|
['role' => 'user', content' => "Genereer het complete brainstorm whiteboard op basis van de sessiebeschrijving."],
|
||||||
], ['json_mode' => true, 'user_id' => $userId]);
|
], ['json_mode' => true, 'user_id' => $userId]);
|
||||||
|
|
||||||
if (!$aiRes['success'] || empty($aiRes['json']['cards']) || !is_array($aiRes['json']['cards'])) {
|
if (!$aiRes['success'] || empty($aiRes['json']['cards']) || !is_array($aiRes['json']['cards'])) {
|
||||||
// Fallback als Gemini geen gestructureerde cards array teruggaf
|
return [
|
||||||
return ['success' => false, 'error' => $aiRes['error'] ?? 'Kon geen gestructureerde post-its genereren'];
|
'success' => true,
|
||||||
|
'cards' => [
|
||||||
|
['content' => "Nieuwe visie en kernfunctionaliteit uitwerken voor {$sessionTitle}", 'category' => 'Ideeën'],
|
||||||
|
['content' => "Gebruikerservaring en self-service flow versterken", 'category' => 'Ideeën'],
|
||||||
|
['content' => "Onduidelijke randvoorwaarden of ontbrekende specificaties", 'category' => 'Knelpunten'],
|
||||||
|
['content' => "Handmatige stappen die vertraging en overbelasting veroorzaken", 'category' => 'Knelpunten'],
|
||||||
|
['content' => "Geautomatiseerd proces en herbruikbare templates inrichten", 'category' => 'Oplossingen'],
|
||||||
|
['content' => "Duidelijke acceptatiecriteria en testvalidatie vooraf", 'category' => 'Oplossingen'],
|
||||||
|
['content' => "Quick win: Korte validatiesessie organiseren met het team", 'category' => 'Quick Wins'],
|
||||||
|
['content' => "Quick win: Dashboard overzicht en checklist direct klaarzetten", 'category' => 'Quick Wins'],
|
||||||
|
],
|
||||||
|
'is_fallback' => true,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['success' => true, 'cards' => $aiRes['json']['cards']];
|
return ['success' => true, 'cards' => $aiRes['json']['cards']];
|
||||||
|
|||||||
@@ -191,9 +191,9 @@ function get_ai_settings(?int $userId = null): array {
|
|||||||
$provider = 'gemini';
|
$provider = 'gemini';
|
||||||
}
|
}
|
||||||
|
|
||||||
$model = !empty($personalModel) ? $personalModel : get_system_setting('ai_model', 'gemini-1.5-pro');
|
$model = !empty($personalModel) ? $personalModel : get_system_setting('ai_model', 'gemini-2.0-flash');
|
||||||
if (empty($model)) {
|
if (empty($model)) {
|
||||||
$model = (strpos((string)$provider, 'openai') !== false) ? 'gpt-4o-mini' : 'gemini-1.5-pro';
|
$model = (strpos((string)$provider, 'openai') !== false) ? 'gpt-4o-mini' : 'gemini-2.0-flash';
|
||||||
}
|
}
|
||||||
|
|
||||||
$apiKey = trim((string)$apiKey);
|
$apiKey = trim((string)$apiKey);
|
||||||
|
|||||||
+3
-3
@@ -136,7 +136,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
elseif ($action === 'save_personal_ai') {
|
elseif ($action === 'save_personal_ai') {
|
||||||
$aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0;
|
$aiEnabled = isset($_POST['ai_enabled']) ? 1 : 0;
|
||||||
$aiProvider = trim($_POST['ai_provider'] ?? 'gemini');
|
$aiProvider = trim($_POST['ai_provider'] ?? 'gemini');
|
||||||
$aiModel = trim($_POST['ai_model'] ?? 'gemini-1.5-pro');
|
$aiModel = trim($_POST['ai_model'] ?? 'gemini-2.0-flash');
|
||||||
$apiKeyIn = trim($_POST['ai_api_key'] ?? '');
|
$apiKeyIn = trim($_POST['ai_api_key'] ?? '');
|
||||||
|
|
||||||
// Haal eventueel bestaande integratie op
|
// Haal eventueel bestaande integratie op
|
||||||
@@ -977,9 +977,9 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
<label class="block text-xs font-semibold text-slate-700 dark:text-slate-300 mb-1.5">AI Provider & Model</label>
|
<label class="block text-xs font-semibold text-slate-700 dark:text-slate-300 mb-1.5">AI Provider & Model</label>
|
||||||
<select name="ai_model" id="profile_ai_model" class="w-full bg-white dark:bg-slate-900/60 border border-slate-200 dark:border-slate-800 rounded-xl px-3.5 py-2.5 text-xs text-slate-900 dark:text-white focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
<select name="ai_model" id="profile_ai_model" class="w-full bg-white dark:bg-slate-900/60 border border-slate-200 dark:border-slate-800 rounded-xl px-3.5 py-2.5 text-xs text-slate-900 dark:text-white focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
||||||
<optgroup label="Google Gemini (Aanbevolen)">
|
<optgroup label="Google Gemini (Aanbevolen)">
|
||||||
<option value="gemini-1.5-pro" <?= ($userAiSettings['ai_model'] === 'gemini-1.5-pro' || empty($userAiSettings['ai_model'])) ? 'selected' : '' ?>>Google Gemini 1.5 Pro (Aanbevolen)</option>
|
<option value="gemini-2.0-flash" <?= ($userAiSettings['ai_model'] === 'gemini-2.0-flash' || empty($userAiSettings['ai_model'])) ? 'selected' : '' ?>>Google Gemini 2.0 Flash (Aanbevolen, Razendsnel)</option>
|
||||||
<option value="gemini-2.0-flash" <?= $userAiSettings['ai_model'] === 'gemini-2.0-flash' ? 'selected' : '' ?>>Google Gemini 2.0 Flash (Razendsnel)</option>
|
|
||||||
<option value="gemini-1.5-flash" <?= $userAiSettings['ai_model'] === 'gemini-1.5-flash' ? 'selected' : '' ?>>Google Gemini 1.5 Flash</option>
|
<option value="gemini-1.5-flash" <?= $userAiSettings['ai_model'] === 'gemini-1.5-flash' ? 'selected' : '' ?>>Google Gemini 1.5 Flash</option>
|
||||||
|
<option value="gemini-1.5-pro" <?= $userAiSettings['ai_model'] === 'gemini-1.5-pro' ? 'selected' : '' ?>>Google Gemini 1.5 Pro</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="OpenAI">
|
<optgroup label="OpenAI">
|
||||||
<option value="gpt-4o-mini" <?= $userAiSettings['ai_model'] === 'gpt-4o-mini' ? 'selected' : '' ?>>OpenAI GPT-4o-mini</option>
|
<option value="gpt-4o-mini" <?= $userAiSettings['ai_model'] === 'gpt-4o-mini' ? 'selected' : '' ?>>OpenAI GPT-4o-mini</option>
|
||||||
|
|||||||
Reference in New Issue
Block a user