235 lines
8.7 KiB
SQL
235 lines
8.7 KiB
SQL
-- Phase 4: Community Detection Schema
|
|||
|
|
-- Extends memory_community with label propagation execution and statistics
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- STEP 1: Create label propagation run tracking
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE IF NOT EXISTS label_propagation_run (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
project_id VARCHAR(255) NOT NULL,
|
||
|
|
run_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
algorithm VARCHAR(50) DEFAULT 'label_propagation',
|
||
|
|
max_iterations INT DEFAULT 10,
|
||
|
|
convergence_threshold FLOAT DEFAULT 0.01,
|
||
|
|
iterations_completed INT,
|
||
|
|
converged BOOLEAN DEFAULT FALSE,
|
||
|
|
|
||
|
|
-- Execution metadata
|
||
|
|
status VARCHAR(20) DEFAULT 'running'
|
||
|
|
CHECK (status IN ('running', 'completed', 'failed')),
|
||
|
|
error_message TEXT,
|
||
|
|
duration_ms INT,
|
||
|
|
|
||
|
|
-- Statistics
|
||
|
|
communities_detected INT,
|
||
|
|
communities_merged INT,
|
||
|
|
communities_split INT,
|
||
|
|
nodes_processed INT,
|
||
|
|
edges_processed INT,
|
||
|
|
|
||
|
|
-- Execution mode
|
||
|
|
dry_run BOOLEAN DEFAULT FALSE,
|
||
|
|
|
||
|
|
CONSTRAINT chk_iterations_valid CHECK (iterations_completed >= 0 AND iterations_completed <= max_iterations)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_label_prop_run_project
|
||
|
|
ON label_propagation_run(project_id, run_at DESC);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_label_prop_run_status
|
||
|
|
ON label_propagation_run(project_id, status)
|
||
|
|
WHERE status IN ('running', 'failed');
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- STEP 2: Create community member map
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE IF NOT EXISTS community_member_map (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
project_id VARCHAR(255) NOT NULL,
|
||
|
|
community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
|
||
|
|
entity_id UUID NOT NULL REFERENCES memory_entity(id) ON DELETE CASCADE,
|
||
|
|
label_propagation_run_id UUID REFERENCES label_propagation_run(id) ON DELETE SET NULL,
|
||
|
|
|
||
|
|
-- Label strength (0-1, higher = stronger membership)
|
||
|
|
label_strength FLOAT DEFAULT 1.0,
|
||
|
|
|
||
|
|
-- Membership tracking
|
||
|
|
is_seed BOOLEAN DEFAULT FALSE,
|
||
|
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
left_at TIMESTAMPTZ,
|
||
|
|
|
||
|
|
-- Consistency
|
||
|
|
CONSTRAINT uq_community_entity_project UNIQUE (project_id, community_id, entity_id),
|
||
|
|
CONSTRAINT chk_label_strength CHECK (label_strength >= 0 AND label_strength <= 1)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_member_project
|
||
|
|
ON community_member_map(project_id, community_id);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_entity_lookup
|
||
|
|
ON community_member_map(entity_id, community_id);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_member_strength
|
||
|
|
ON community_member_map(community_id, label_strength DESC)
|
||
|
|
WHERE left_at IS NULL;
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_seeds
|
||
|
|
ON community_member_map(project_id, is_seed)
|
||
|
|
WHERE is_seed = TRUE;
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- STEP 3: Create community statistics table
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE IF NOT EXISTS community_statistics (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
project_id VARCHAR(255) NOT NULL,
|
||
|
|
community_id UUID NOT NULL UNIQUE REFERENCES memory_community(id) ON DELETE CASCADE,
|
||
|
|
label_propagation_run_id UUID NOT NULL REFERENCES label_propagation_run(id) ON DELETE CASCADE,
|
||
|
|
|
||
|
|
-- Membership stats
|
||
|
|
member_count INT DEFAULT 0,
|
||
|
|
active_member_count INT DEFAULT 0,
|
||
|
|
seed_member_count INT DEFAULT 0,
|
||
|
|
|
||
|
|
-- Graph structure
|
||
|
|
internal_edge_count INT DEFAULT 0,
|
||
|
|
external_edge_count INT DEFAULT 0,
|
||
|
|
|
||
|
|
-- Cohesion metrics
|
||
|
|
density FLOAT DEFAULT 0.0,
|
||
|
|
modularity FLOAT DEFAULT 0.0,
|
||
|
|
|
||
|
|
-- Edge types within community
|
||
|
|
relation_type_distribution JSONB DEFAULT '{}',
|
||
|
|
|
||
|
|
-- Temporal metrics
|
||
|
|
first_entity_created TIMESTAMPTZ,
|
||
|
|
last_entity_accessed TIMESTAMPTZ,
|
||
|
|
avg_entity_age_days FLOAT DEFAULT 0.0,
|
||
|
|
|
||
|
|
-- Quality scores
|
||
|
|
coherence_score FLOAT DEFAULT 0.5,
|
||
|
|
stability_score FLOAT DEFAULT 0.5,
|
||
|
|
significance_score FLOAT DEFAULT 0.5,
|
||
|
|
|
||
|
|
CONSTRAINT chk_stats_nonnegative CHECK (
|
||
|
|
member_count >= 0 AND
|
||
|
|
internal_edge_count >= 0 AND
|
||
|
|
external_edge_count >= 0
|
||
|
|
),
|
||
|
|
CONSTRAINT chk_stats_bounded CHECK (
|
||
|
|
density >= 0 AND density <= 1 AND
|
||
|
|
modularity >= -1 AND modularity <= 1 AND
|
||
|
|
coherence_score >= 0 AND coherence_score <= 1 AND
|
||
|
|
stability_score >= 0 AND stability_score <= 1 AND
|
||
|
|
significance_score >= 0 AND significance_score <= 1
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_stats_project
|
||
|
|
ON community_statistics(project_id, community_id);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_stats_run
|
||
|
|
ON community_statistics(label_propagation_run_id);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_stats_quality
|
||
|
|
ON community_statistics(project_id, coherence_score DESC, significance_score DESC)
|
||
|
|
WHERE coherence_score > 0.7;
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- STEP 4: Create community merge history
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE IF NOT EXISTS community_merge_history (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
project_id VARCHAR(255) NOT NULL,
|
||
|
|
source_community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
|
||
|
|
target_community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
|
||
|
|
merge_reason VARCHAR(100),
|
||
|
|
merged_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
label_propagation_run_id UUID REFERENCES label_propagation_run(id) ON DELETE SET NULL,
|
||
|
|
|
||
|
|
-- Rollback capability
|
||
|
|
dry_run BOOLEAN DEFAULT FALSE,
|
||
|
|
|
||
|
|
-- Statistics before merge
|
||
|
|
source_member_count INT,
|
||
|
|
target_member_count INT,
|
||
|
|
|
||
|
|
-- Impact
|
||
|
|
members_moved INT,
|
||
|
|
edges_reattached INT
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_merge_history_project
|
||
|
|
ON community_merge_history(project_id, merged_at DESC);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_merge_history_communities
|
||
|
|
ON community_merge_history(source_community_id, target_community_id);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- STEP 5: Add community detection status to memory_community
|
||
|
|
-- ============================================
|
||
|
|
ALTER TABLE memory_community
|
||
|
|
ADD COLUMN IF NOT EXISTS last_detection_run_id UUID REFERENCES label_propagation_run(id) ON DELETE SET NULL,
|
||
|
|
ADD COLUMN IF NOT EXISTS detection_score FLOAT DEFAULT 0.5,
|
||
|
|
ADD COLUMN IF NOT EXISTS is_permanent BOOLEAN DEFAULT FALSE,
|
||
|
|
ADD COLUMN IF NOT EXISTS merge_into_id UUID REFERENCES memory_community(id) ON DELETE SET NULL;
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_detection_run
|
||
|
|
ON memory_community(last_detection_run_id, detection_score DESC)
|
||
|
|
WHERE detection_score > 0.7;
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- STEP 6: Add community-level summary generation tracking
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE IF NOT EXISTS community_summary_generation (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
project_id VARCHAR(255) NOT NULL,
|
||
|
|
community_id UUID NOT NULL REFERENCES memory_community(id) ON DELETE CASCADE,
|
||
|
|
generated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
generated_by VARCHAR(255),
|
||
|
|
|
||
|
|
-- LLM usage
|
||
|
|
llm_model VARCHAR(100),
|
||
|
|
input_tokens INT,
|
||
|
|
output_tokens INT,
|
||
|
|
cost_usd FLOAT,
|
||
|
|
|
||
|
|
-- Generation method
|
||
|
|
method VARCHAR(50) DEFAULT 'extractive', -- 'extractive' or 'abstractive'
|
||
|
|
|
||
|
|
-- Quality
|
||
|
|
coherence_rating INT CHECK (coherence_rating >= 1 AND coherence_rating <= 5),
|
||
|
|
user_feedback TEXT,
|
||
|
|
|
||
|
|
-- Result
|
||
|
|
summary_text TEXT NOT NULL,
|
||
|
|
summary_embedding VECTOR(768),
|
||
|
|
|
||
|
|
-- Versioning
|
||
|
|
version INT DEFAULT 1,
|
||
|
|
is_latest BOOLEAN DEFAULT TRUE
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_summary_latest
|
||
|
|
ON community_summary_generation(community_id, generated_at DESC)
|
||
|
|
WHERE is_latest = TRUE;
|
||
|
|
|
||
|
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_community_summary_embedding
|
||
|
|
ON community_summary_generation USING hnsw (summary_embedding vector_cosine_ops)
|
||
|
|
WITH (m = 16, ef_construction = 200)
|
||
|
|
WHERE is_latest = TRUE;
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- ROLLBACK INSTRUCTIONS
|
||
|
|
-- ============================================
|
||
|
|
-- DROP TABLE IF EXISTS community_summary_generation;
|
||
|
|
-- DROP TABLE IF EXISTS community_merge_history;
|
||
|
|
-- DROP TABLE IF EXISTS community_statistics;
|
||
|
|
-- DROP TABLE IF EXISTS community_member_map;
|
||
|
|
-- DROP TABLE IF EXISTS label_propagation_run;
|
||
|
|
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS last_detection_run_id;
|
||
|
|
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS detection_score;
|
||
|
|
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS is_permanent;
|
||
|
|
-- ALTER TABLE memory_community DROP COLUMN IF EXISTS merge_into_id;
|