128 lines
4.4 KiB
PL/PgSQL
128 lines
4.4 KiB
PL/PgSQL
-- Migration 033: AI Chat Schema
|
|
-- Creates tables for the AI chat assistant extension:
|
|
-- chat_sessions, chat_messages, knowledge_documents, and match_documents RPC
|
|
|
|
-- Enable pgvector for embedding storage
|
|
create extension if not exists vector with schema extensions;
|
|
|
|
-- ============================================================
|
|
-- chat_sessions
|
|
-- ============================================================
|
|
|
|
create table public.chat_sessions (
|
|
id uuid primary key default gen_random_uuid(),
|
|
user_id uuid references auth.users on delete cascade not null,
|
|
title text,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
alter table public.chat_sessions enable row level security;
|
|
|
|
create policy "chat_sessions_select" on public.chat_sessions
|
|
for select using (auth.uid() = user_id);
|
|
create policy "chat_sessions_insert" on public.chat_sessions
|
|
for insert with check (auth.uid() = user_id);
|
|
create policy "chat_sessions_update" on public.chat_sessions
|
|
for update using (auth.uid() = user_id);
|
|
create policy "chat_sessions_delete" on public.chat_sessions
|
|
for delete using (auth.uid() = user_id);
|
|
|
|
create index idx_chat_sessions_user_created on public.chat_sessions (user_id, created_at desc);
|
|
|
|
create trigger chat_sessions_updated_at
|
|
before update on public.chat_sessions
|
|
for each row execute function public.update_updated_at_column();
|
|
|
|
-- ============================================================
|
|
-- chat_messages
|
|
-- ============================================================
|
|
|
|
create table public.chat_messages (
|
|
id uuid primary key default gen_random_uuid(),
|
|
session_id uuid references public.chat_sessions on delete cascade not null,
|
|
user_id uuid references auth.users on delete cascade not null,
|
|
role text not null check (role in ('user', 'assistant')),
|
|
content text not null,
|
|
sources jsonb,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
alter table public.chat_messages enable row level security;
|
|
|
|
create policy "chat_messages_select" on public.chat_messages
|
|
for select using (auth.uid() = user_id);
|
|
create policy "chat_messages_insert" on public.chat_messages
|
|
for insert with check (auth.uid() = user_id);
|
|
create policy "chat_messages_update" on public.chat_messages
|
|
for update using (auth.uid() = user_id);
|
|
create policy "chat_messages_delete" on public.chat_messages
|
|
for delete using (auth.uid() = user_id);
|
|
|
|
create index idx_chat_messages_session on public.chat_messages (session_id, created_at);
|
|
|
|
-- ============================================================
|
|
-- knowledge_documents
|
|
-- ============================================================
|
|
|
|
create table public.knowledge_documents (
|
|
id uuid primary key default gen_random_uuid(),
|
|
source_file text not null,
|
|
title text not null,
|
|
section_title text,
|
|
content text not null,
|
|
content_hash text unique not null,
|
|
embedding extensions.vector(1536),
|
|
metadata jsonb default '{}',
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
alter table public.knowledge_documents enable row level security;
|
|
|
|
-- Knowledge documents are shared — any authenticated user can read
|
|
create policy "knowledge_documents_select" on public.knowledge_documents
|
|
for select using (true);
|
|
|
|
create index idx_knowledge_documents_hash on public.knowledge_documents (content_hash);
|
|
|
|
-- ============================================================
|
|
-- match_documents RPC (vector similarity search)
|
|
-- ============================================================
|
|
|
|
create or replace function public.match_documents(
|
|
query_embedding extensions.vector,
|
|
match_count int default 5,
|
|
match_threshold float default 0.7
|
|
)
|
|
returns table (
|
|
id uuid,
|
|
source_file text,
|
|
title text,
|
|
section_title text,
|
|
content text,
|
|
metadata jsonb,
|
|
similarity float
|
|
)
|
|
language plpgsql
|
|
security definer
|
|
set search_path = public, extensions
|
|
as $$
|
|
begin
|
|
return query
|
|
select
|
|
kd.id,
|
|
kd.source_file,
|
|
kd.title,
|
|
kd.section_title,
|
|
kd.content,
|
|
kd.metadata,
|
|
1 - (kd.embedding <=> query_embedding)::float as similarity
|
|
from public.knowledge_documents kd
|
|
where 1 - (kd.embedding <=> query_embedding) >= match_threshold
|
|
order by kd.embedding <=> query_embedding
|
|
limit match_count;
|
|
end;
|
|
$$;
|
|
|
|
grant execute on function public.match_documents(extensions.vector, int, float) to authenticated;
|