system-prompts-and-models-o.../dealix/supabase/migrations/202605010001_v3_project_memory.sql
2026-05-01 14:03:52 +03:00

140 lines
4.8 KiB
PL/PgSQL

-- Dealix v3 — Project + strategic memory on Postgres/pgvector
-- Apply via Supabase SQL editor or `supabase db push` after linking the project.
-- RLS: enabled with no broad policies by default — use service role from backend only.
create extension if not exists vector;
-- ── Core tables ─────────────────────────────────────────────
create table if not exists public.project_documents (
id uuid primary key default gen_random_uuid(),
path text not null,
source_type text,
content_hash text not null,
metadata jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now(),
unique (path, content_hash)
);
create table if not exists public.project_chunks (
id uuid primary key default gen_random_uuid(),
document_id uuid not null references public.project_documents (id) on delete cascade,
chunk_index int not null,
content text not null,
embedding vector(384),
metadata jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now()
);
create table if not exists public.strategic_memory (
id uuid primary key default gen_random_uuid(),
category text,
content text not null,
embedding vector(384),
metadata jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now()
);
-- ── Similarity search helpers (cosine distance) ─────────────
create or replace function public.match_project_chunks(
query_embedding vector(384),
match_count int default 12,
min_similarity float default 0.05
)
returns table (
id uuid,
document_id uuid,
chunk_index int,
content text,
similarity float
)
language sql
stable
parallel safe
as $$
select
pc.id,
pc.document_id,
pc.chunk_index,
pc.content,
(1 - (pc.embedding <=> query_embedding))::float as similarity
from public.project_chunks pc
where pc.embedding is not null
and (1 - (pc.embedding <=> query_embedding)) >= min_similarity
order by pc.embedding <=> query_embedding
limit greatest(match_count, 1);
$$;
create or replace function public.match_strategic_memory(
query_embedding vector(384),
match_count int default 12,
min_similarity float default 0.05
)
returns table (
id uuid,
category text,
content text,
similarity float
)
language sql
stable
parallel safe
as $$
select
sm.id,
sm.category,
sm.content,
(1 - (sm.embedding <=> query_embedding))::float as similarity
from public.strategic_memory sm
where sm.embedding is not null
and (1 - (sm.embedding <=> query_embedding)) >= min_similarity
order by sm.embedding <=> query_embedding
limit greatest(match_count, 1);
$$;
-- ── Indexes ─────────────────────────────────────────────────
create index if not exists project_documents_path_idx on public.project_documents (path);
create index if not exists project_chunks_document_idx on public.project_chunks (document_id);
-- HNSW (Supabase 15+); falls back to creating via dashboard if version lacks operator class.
create index if not exists project_chunks_embedding_hnsw
on public.project_chunks
using hnsw (embedding vector_cosine_ops);
create index if not exists strategic_memory_embedding_hnsw
on public.strategic_memory
using hnsw (embedding vector_cosine_ops);
-- ── Row Level Security ──────────────────────────────────────
-- No client-side anon access to embeddings. Backend uses service role.
alter table public.project_documents enable row level security;
alter table public.project_chunks enable row level security;
alter table public.strategic_memory enable row level security;
-- Example policies (KEEP COMMENTED — enable only with explicit security review):
--
-- create policy "service_role_all_documents"
-- on public.project_documents
-- for all
-- using (auth.role() = 'service_role')
-- with check (auth.role() = 'service_role');
--
-- create policy "service_role_all_chunks"
-- on public.project_chunks
-- for all
-- using (auth.role() = 'service_role')
-- with check (auth.role() = 'service_role');
--
-- create policy "service_role_all_strategic"
-- on public.strategic_memory
-- for all
-- using (auth.role() = 'service_role')
-- with check (auth.role() = 'service_role');
comment on table public.project_documents is 'Source files / docs indexed for Dealix Project Intelligence.';
comment on table public.project_chunks is 'Chunked text + optional pgvector embedding; never store raw secrets.';
comment on table public.strategic_memory is 'Strategic notes and decisions for operator + agents.';