'use client'; import React, { useState, useEffect } from 'react'; import Link from 'next/link'; import { useAuth } from '@/contexts/AuthContext'; import { useRouter } from 'next/navigation'; import { BB_PEN_NAMES, AV_PEN_NAMES } from '@/lib/auth/permissions'; interface Settings { ollama_endpoint: string; ollama_model_default: string; ollama_model_translation: string; ollama_model_background: string; auto_failover: boolean; failover_timeout_seconds: number; default_site_id: number; default_pen_name_bb: string; default_pen_name_av: string; save_session_history: boolean; history_retention_days: number; streaming_enabled: boolean; background_use_local: boolean; background_timeout_seconds: number; priority_queue_enabled: boolean; } interface ConnectionTest { online: boolean; latencyMs: number; models: string[]; } export default function AIConsoleSettingsPage() { const { user, loading } = useAuth(); const router = useRouter(); const [settings, setSettings] = useState({ ollama_endpoint: '', ollama_model_default: '', ollama_model_translation: '', ollama_model_background: '', auto_failover: true, failover_timeout_seconds: 8, default_site_id: 1, default_pen_name_bb: 'Michael Strand', default_pen_name_av: 'Rex Chandler', save_session_history: true, history_retention_days: 30, streaming_enabled: true, background_use_local: true, background_timeout_seconds: 120, priority_queue_enabled: true, }); const [loadingSettings, setLoadingSettings] = useState(true); const [saving, setSaving] = useState(false); const [testing, setTesting] = useState(false); const [connectionResult, setConnectionResult] = useState(null); const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null); const showToast = (message: string, type: 'success' | 'error' = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 4000); }; useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]); useEffect(() => { if (!user) return; fetch('/api/ai/console-settings') .then(r => r.json()) .then(data => { if (data.settings) setSettings(data.settings); }) .catch(() => {}) .finally(() => setLoadingSettings(false)); }, [user]); const handleTestConnection = async () => { setTesting(true); setConnectionResult(null); try { const res = await fetch('/api/ai/console-settings?action=test_connection'); const data = await res.json(); setConnectionResult(data); } catch { setConnectionResult({ online: false, latencyMs: 0, models: [] }); } finally { setTesting(false); } }; const handleSave = async () => { setSaving(true); try { const res = await fetch('/api/ai/console-settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'save_settings', ...settings }), }); const data = await res.json(); if (data.success) showToast('Settings saved'); else showToast(data.error || 'Failed to save', 'error'); } catch { showToast('Failed to save', 'error'); } finally { setSaving(false); } }; const update = (key: keyof Settings, value: any) => { setSettings(prev => ({ ...prev, [key]: value })); }; if (loading || loadingSettings) { return (
); } return (
{/* Header */}
← AI Console /

AI Console Settings

{/* Local AI Server */}

Local AI Server (Ollama)

Connect to your local Ollama instance running on your Windows PC with 24GB GPU.

{/* Windows sleep warning */}

Windows Sleep / Hibernate Warning

Your Windows server may sleep or hibernate when inactive, causing the local AI to go offline. To prevent this:

  • Option 1: Windows Settings → System → Power & Sleep → Sleep → set to Never
  • Option 2: Download the free Caffeine app for Windows — keeps your PC awake automatically

The Health Dashboard will alert you if the local server has been offline for more than 30 minutes.

update('ollama_endpoint', e.target.value)} placeholder="http://192.168.1.x:11434 or https://your-tunnel.ngrok.io" className="w-full bg-[#0d1117] border border-[#2a3a50] text-white text-sm rounded-lg px-3 py-2.5 focus:outline-none focus:border-[#3b82f6] placeholder-[#333]" />

Use direct IP if on the same network, or a tunnel URL (ngrok, Cloudflare Tunnel, Tailscale) for remote access.

{connectionResult && (
{connectionResult.online ? `Connected — ${connectionResult.latencyMs}ms — ${connectionResult.models.length} model(s)` : 'Unreachable'}
)}
{connectionResult?.models && connectionResult.models.length > 0 && (
)}

Auto-failover to Cloud AI

Automatically use Anthropic when local is unavailable

update('failover_timeout_seconds', Number(e.target.value))} min={3} max={30} className="w-32 bg-[#0d1117] border border-[#2a3a50] text-white text-sm rounded-lg px-3 py-2 focus:outline-none focus:border-[#3b82f6]" />
{/* Cloud AI */}

Cloud AI (Anthropic Failover)

Used when local Ollama is unavailable.

Model claude-sonnet-4-20250514
Fallback Model claude-3-5-sonnet-20241022
API Key Status {/* Key status shown via ping */} Configured
{/* Console Preferences */}

AI Console Preferences

Save Session History

Store last 20 sessions

Streaming Responses

Text appears as it generates

{/* Background Tasks */}

Background AI Tasks

Route Background Tasks to Local AI

Story generation, translation, classification

update('background_timeout_seconds', Number(e.target.value))} min={30} max={300} className="w-32 bg-[#0d1117] border border-[#2a3a50] text-white text-sm rounded-lg px-3 py-2 focus:outline-none focus:border-[#3b82f6]" />

Priority Queue

Interactive requests take priority over background tasks

Priority Queue Order

    {[ 'Interactive AI Console requests (admin typing)', 'Breaking news story generation', 'Show coverage auto-stories during show week', 'Standard background auto-stories', 'Translation backfill', 'Classification and extraction tasks', ].map((item, i) => (
  1. {i + 1} {item}
  2. ))}
{toast && (
{toast.message}
)}
); }