99 lines
4.6 KiB
SQL
99 lines
4.6 KiB
SQL
-- 008_knowledge_bases.sql
|
|
-- Knowledge Bases: KB metadata, documents, chunks, channel links.
|
|
-- Requires: pgvector extension already installed (via db-bootstrap.sh).
|
|
|
|
-- ── Knowledge Bases ──────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS knowledge_bases (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
scope TEXT NOT NULL DEFAULT 'global', -- global, team, personal
|
|
owner_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
|
|
|
|
-- Snapshot of embedding config at creation time.
|
|
-- Changing the model requires a full re-embed (rebuild endpoint).
|
|
-- { "provider_config_id": "...", "model_id": "...", "dimensions": 1536 }
|
|
embedding_config JSONB NOT NULL DEFAULT '{}',
|
|
|
|
-- Denormalized stats, updated by ingest pipeline.
|
|
document_count INT NOT NULL DEFAULT 0,
|
|
chunk_count INT NOT NULL DEFAULT 0,
|
|
total_bytes BIGINT NOT NULL DEFAULT 0,
|
|
|
|
status TEXT NOT NULL DEFAULT 'active', -- active, processing, error
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
|
|
CONSTRAINT kb_scope_check CHECK (
|
|
(scope = 'global' AND owner_id IS NULL) OR
|
|
(scope = 'team' AND team_id IS NOT NULL) OR
|
|
(scope = 'personal' AND owner_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_kb_scope ON knowledge_bases(scope);
|
|
CREATE INDEX IF NOT EXISTS idx_kb_owner ON knowledge_bases(owner_id) WHERE owner_id IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_kb_team ON knowledge_bases(team_id) WHERE team_id IS NOT NULL;
|
|
|
|
-- ── KB Documents ─────────────────────────────
|
|
-- One row per uploaded file. Blobs live in ObjectStore under:
|
|
-- kb/{kb_id}/{doc_id}_{filename}
|
|
|
|
CREATE TABLE IF NOT EXISTS kb_documents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
kb_id UUID NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
|
filename TEXT NOT NULL,
|
|
content_type TEXT NOT NULL,
|
|
size_bytes BIGINT NOT NULL,
|
|
storage_key TEXT NOT NULL,
|
|
extracted_text TEXT, -- full text for re-chunking
|
|
chunk_count INT NOT NULL DEFAULT 0,
|
|
status TEXT NOT NULL DEFAULT 'pending', -- pending, chunking, embedding, ready, error
|
|
error TEXT,
|
|
uploaded_by UUID NOT NULL REFERENCES users(id),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_kbdoc_kb ON kb_documents(kb_id);
|
|
|
|
-- ── KB Chunks ────────────────────────────────
|
|
-- Chunked text + embedding vector for similarity search.
|
|
-- vector(3072) accommodates all current embedding models:
|
|
-- OpenAI ada-002 / text-embedding-3-small = 1536
|
|
-- OpenAI text-embedding-3-large = 3072
|
|
-- Open-source models = typically 768-1024
|
|
-- Smaller vectors are zero-padded at insert time.
|
|
|
|
CREATE TABLE IF NOT EXISTS kb_chunks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
kb_id UUID NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
|
document_id UUID NOT NULL REFERENCES kb_documents(id) ON DELETE CASCADE,
|
|
chunk_index INT NOT NULL,
|
|
content TEXT NOT NULL,
|
|
token_count INT NOT NULL DEFAULT 0,
|
|
embedding VECTOR(3072),
|
|
metadata JSONB NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_kbchunk_kb ON kb_chunks(kb_id);
|
|
CREATE INDEX IF NOT EXISTS idx_kbchunk_doc ON kb_chunks(document_id);
|
|
|
|
-- NOTE: IVFFlat index for similarity search is created after first data
|
|
-- load (needs rows to train). The ingest handler creates it:
|
|
-- CREATE INDEX idx_kbchunk_embedding ON kb_chunks
|
|
-- USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
|
|
|
-- ── Channel-KB Links ─────────────────────────
|
|
-- Which KBs are active for a given channel.
|
|
|
|
CREATE TABLE IF NOT EXISTS channel_knowledge_bases (
|
|
channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
kb_id UUID NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
|
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
added_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (channel_id, kb_id)
|
|
); |