Files
avbeat-com/supabase/migrations/20260322250000_ai_console_hybrid_router.sql
2026-05-07 16:39:17 +00:00

165 lines
6.4 KiB
SQL

-- ============================================================
-- AI Console & Hybrid AI Routing — Database Schema
-- Sections A-D: AI Console, Sessions, Health Log, Settings
-- ============================================================
-- ─── AI Console Settings ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.ai_console_settings (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
ollama_endpoint text NOT NULL DEFAULT '',
ollama_model_default text NOT NULL DEFAULT '',
ollama_model_translation text NOT NULL DEFAULT '',
ollama_model_background text NOT NULL DEFAULT '',
auto_failover boolean NOT NULL DEFAULT true,
failover_timeout_seconds integer NOT NULL DEFAULT 8,
default_site_id integer NOT NULL DEFAULT 1,
default_pen_name_bb text NOT NULL DEFAULT 'Michael Strand',
default_pen_name_av text NOT NULL DEFAULT 'Rex Chandler',
save_session_history boolean NOT NULL DEFAULT true,
history_retention_days integer NOT NULL DEFAULT 30,
streaming_enabled boolean NOT NULL DEFAULT true,
background_use_local boolean NOT NULL DEFAULT true,
background_timeout_seconds integer NOT NULL DEFAULT 120,
priority_queue_enabled boolean NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
-- ─── AI Console Sessions ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.ai_console_sessions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
site_id integer NOT NULL DEFAULT 1,
pen_name text,
first_message_preview text NOT NULL DEFAULT '',
provider_used text NOT NULL DEFAULT 'anthropic',
model_used text NOT NULL DEFAULT 'claude-sonnet-4-20250514',
last_message_at timestamptz NOT NULL DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_ai_console_sessions_user_id
ON public.ai_console_sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_ai_console_sessions_last_message
ON public.ai_console_sessions(last_message_at DESC);
-- ─── AI Console Messages ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.ai_console_messages (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
session_id uuid NOT NULL REFERENCES public.ai_console_sessions(id) ON DELETE CASCADE,
role text NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
content text NOT NULL DEFAULT '',
provider_used text,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_ai_console_messages_session_id
ON public.ai_console_messages(session_id);
-- ─── AI Health Log ────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS public.ai_health_log (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
provider text NOT NULL CHECK (provider IN ('ollama', 'anthropic')),
success boolean NOT NULL DEFAULT true,
latency_ms integer NOT NULL DEFAULT 0,
model_used text NOT NULL DEFAULT '',
priority integer NOT NULL DEFAULT 4,
error_message text,
is_failover boolean NOT NULL DEFAULT false,
logged_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_ai_health_log_logged_at
ON public.ai_health_log(logged_at DESC);
CREATE INDEX IF NOT EXISTS idx_ai_health_log_provider
ON public.ai_health_log(provider, logged_at DESC);
-- Auto-purge logs older than 30 days (keep table lean)
-- This is handled by a periodic cleanup; no trigger needed for now.
-- ─── RLS Policies ────────────────────────────────────────────
-- ai_console_settings: admin read/write only
ALTER TABLE public.ai_console_settings ENABLE ROW LEVEL SECURITY;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE tablename = 'ai_console_settings' AND policyname = 'admin_all'
) THEN
CREATE POLICY admin_all ON public.ai_console_settings
FOR ALL
USING (
EXISTS (
SELECT 1 FROM public.user_profiles
WHERE id = auth.uid() AND role IN ('administrator', 'admin')
)
);
END IF;
END $$;
-- ai_console_sessions: users own their sessions
ALTER TABLE public.ai_console_sessions ENABLE ROW LEVEL SECURITY;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE tablename = 'ai_console_sessions' AND policyname = 'owner_all'
) THEN
CREATE POLICY owner_all ON public.ai_console_sessions
FOR ALL
USING (user_id = auth.uid());
END IF;
END $$;
-- ai_console_messages: users own messages via session ownership
ALTER TABLE public.ai_console_messages ENABLE ROW LEVEL SECURITY;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE tablename = 'ai_console_messages' AND policyname = 'owner_via_session'
) THEN
CREATE POLICY owner_via_session ON public.ai_console_messages
FOR ALL
USING (
EXISTS (
SELECT 1 FROM public.ai_console_sessions
WHERE id = ai_console_messages.session_id AND user_id = auth.uid()
)
);
END IF;
END $$;
-- ai_health_log: admin read, service role write (health-log route uses service role implicitly)
ALTER TABLE public.ai_health_log ENABLE ROW LEVEL SECURITY;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE tablename = 'ai_health_log' AND policyname = 'admin_read'
) THEN
CREATE POLICY admin_read ON public.ai_health_log
FOR SELECT
USING (
EXISTS (
SELECT 1 FROM public.user_profiles
WHERE id = auth.uid() AND role IN ('administrator', 'admin')
)
);
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE tablename = 'ai_health_log' AND policyname = 'service_insert'
) THEN
CREATE POLICY service_insert ON public.ai_health_log
FOR INSERT
WITH CHECK (true);
END IF;
END $$;