-- ============================================ -- UoV AI Assistant - Supabase Database Schema -- ============================================ -- Enable UUID extension CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- ============================================ -- Table: chat_sessions -- Purpose: Store chat session metadata -- ============================================ CREATE TABLE IF NOT EXISTS chat_sessions ( session_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), created_at TIMESTAMPTZ DEFAULT NOW() ); -- Index for faster lookups CREATE INDEX IF NOT EXISTS idx_chat_sessions_created_at ON chat_sessions(created_at DESC); -- ============================================ -- Table: chat_messages -- Purpose: Store all chat messages (user + assistant) -- ============================================ CREATE TABLE IF NOT EXISTS chat_messages ( message_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), session_id UUID NOT NULL REFERENCES chat_sessions(session_id) ON DELETE CASCADE, role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'assistant')), content TEXT NOT NULL, citations JSONB, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Indexes for faster queries CREATE INDEX IF NOT EXISTS idx_chat_messages_session_id ON chat_messages(session_id); CREATE INDEX IF NOT EXISTS idx_chat_messages_created_at ON chat_messages(created_at DESC); -- ============================================ -- Table: feedback -- Purpose: Store user feedback on assistant responses -- ============================================ CREATE TABLE IF NOT EXISTS feedback ( feedback_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), session_id UUID NOT NULL REFERENCES chat_sessions(session_id) ON DELETE CASCADE, message_id UUID NOT NULL REFERENCES chat_messages(message_id) ON DELETE CASCADE, rating VARCHAR(10) NOT NULL CHECK (rating IN ('up', 'down')), comment TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Indexes for analytics CREATE INDEX IF NOT EXISTS idx_feedback_session_id ON feedback(session_id); CREATE INDEX IF NOT EXISTS idx_feedback_message_id ON feedback(message_id); CREATE INDEX IF NOT EXISTS idx_feedback_rating ON feedback(rating); CREATE INDEX IF NOT EXISTS idx_feedback_created_at ON feedback(created_at DESC); -- ============================================ -- Table: request_logs -- Purpose: Store API request metadata for monitoring -- ============================================ CREATE TABLE IF NOT EXISTS request_logs ( request_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), session_id UUID REFERENCES chat_sessions(session_id) ON DELETE SET NULL, endpoint VARCHAR(100), latency_ms INTEGER, error TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Indexes for monitoring queries CREATE INDEX IF NOT EXISTS idx_request_logs_session_id ON request_logs(session_id); CREATE INDEX IF NOT EXISTS idx_request_logs_endpoint ON request_logs(endpoint); CREATE INDEX IF NOT EXISTS idx_request_logs_created_at ON request_logs(created_at DESC); CREATE INDEX IF NOT EXISTS idx_request_logs_error ON request_logs(error) WHERE error IS NOT NULL; -- ============================================ -- Table: documents (optional metadata) -- Purpose: Store document metadata for ingested files -- ============================================ CREATE TABLE IF NOT EXISTS documents ( document_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), title VARCHAR(255) NOT NULL, source_file VARCHAR(500) NOT NULL, file_type VARCHAR(50), total_chunks INTEGER, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Index for document lookups CREATE INDEX IF NOT EXISTS idx_documents_source_file ON documents(source_file); CREATE INDEX IF NOT EXISTS idx_documents_created_at ON documents(created_at DESC); -- ============================================ -- Views for Analytics -- ============================================ -- View: Session statistics CREATE OR REPLACE VIEW session_stats AS SELECT cs.session_id, cs.created_at, COUNT(cm.message_id) as message_count, COUNT(CASE WHEN cm.role = 'user' THEN 1 END) as user_messages, COUNT(CASE WHEN cm.role = 'assistant' THEN 1 END) as assistant_messages, COUNT(f.feedback_id) as feedback_count, COUNT(CASE WHEN f.rating = 'up' THEN 1 END) as positive_feedback, COUNT(CASE WHEN f.rating = 'down' THEN 1 END) as negative_feedback FROM chat_sessions cs LEFT JOIN chat_messages cm ON cs.session_id = cm.session_id LEFT JOIN feedback f ON cs.session_id = f.session_id GROUP BY cs.session_id, cs.created_at; -- View: Daily metrics CREATE OR REPLACE VIEW daily_metrics AS SELECT DATE(created_at) as date, COUNT(DISTINCT session_id) as total_sessions, COUNT(*) as total_messages FROM chat_messages GROUP BY DATE(created_at) ORDER BY date DESC; -- ============================================ -- Comments for documentation -- ============================================ COMMENT ON TABLE chat_sessions IS 'Stores chat session metadata'; COMMENT ON TABLE chat_messages IS 'Stores all chat messages with citations'; COMMENT ON TABLE feedback IS 'Stores user feedback on assistant responses'; COMMENT ON TABLE request_logs IS 'Stores API request logs for monitoring'; COMMENT ON TABLE documents IS 'Stores metadata for ingested documents';