@twelvehart/supermemory-runtime 1.0.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +57 -0
- package/README.md +374 -0
- package/dist/index.js +189 -0
- package/dist/mcp/index.js +1132 -0
- package/docker-compose.prod.yml +91 -0
- package/docker-compose.yml +358 -0
- package/drizzle/0000_dapper_the_professor.sql +159 -0
- package/drizzle/0001_api_keys.sql +51 -0
- package/drizzle/meta/0000_snapshot.json +1532 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +20 -0
- package/package.json +114 -0
- package/scripts/add-extraction-job.ts +122 -0
- package/scripts/benchmark-pgvector.ts +122 -0
- package/scripts/bootstrap.sh +209 -0
- package/scripts/check-runtime-pack.ts +111 -0
- package/scripts/claude-mcp-config.ts +336 -0
- package/scripts/docker-entrypoint.sh +183 -0
- package/scripts/doctor.ts +377 -0
- package/scripts/init-db.sql +33 -0
- package/scripts/install.sh +1110 -0
- package/scripts/mcp-setup.ts +271 -0
- package/scripts/migrations/001_create_pgvector_extension.sql +31 -0
- package/scripts/migrations/002_create_memory_embeddings_table.sql +75 -0
- package/scripts/migrations/003_create_hnsw_index.sql +94 -0
- package/scripts/migrations/004_create_memory_embeddings_standalone.sql +70 -0
- package/scripts/migrations/005_create_chunks_table.sql +95 -0
- package/scripts/migrations/006_create_processing_queue.sql +45 -0
- package/scripts/migrations/generate_test_data.sql +42 -0
- package/scripts/migrations/phase1_comprehensive_test.sql +204 -0
- package/scripts/migrations/run_migrations.sh +286 -0
- package/scripts/migrations/test_hnsw_index.sql +255 -0
- package/scripts/pre-commit-secrets +282 -0
- package/scripts/run-extraction-worker.ts +46 -0
- package/scripts/run-phase1-tests.sh +291 -0
- package/scripts/setup.ts +222 -0
- package/scripts/smoke-install.sh +12 -0
- package/scripts/test-health-endpoint.sh +328 -0
- package/src/api/index.ts +2 -0
- package/src/api/middleware/auth.ts +80 -0
- package/src/api/middleware/csrf.ts +308 -0
- package/src/api/middleware/errorHandler.ts +166 -0
- package/src/api/middleware/rateLimit.ts +360 -0
- package/src/api/middleware/validation.ts +514 -0
- package/src/api/routes/documents.ts +286 -0
- package/src/api/routes/profiles.ts +237 -0
- package/src/api/routes/search.ts +71 -0
- package/src/api/stores/index.ts +58 -0
- package/src/config/bootstrap-env.ts +3 -0
- package/src/config/env.ts +71 -0
- package/src/config/feature-flags.ts +25 -0
- package/src/config/index.ts +140 -0
- package/src/config/secrets.config.ts +291 -0
- package/src/db/client.ts +92 -0
- package/src/db/index.ts +73 -0
- package/src/db/postgres.ts +72 -0
- package/src/db/schema/chunks.schema.ts +31 -0
- package/src/db/schema/containers.schema.ts +46 -0
- package/src/db/schema/documents.schema.ts +49 -0
- package/src/db/schema/embeddings.schema.ts +32 -0
- package/src/db/schema/index.ts +11 -0
- package/src/db/schema/memories.schema.ts +72 -0
- package/src/db/schema/profiles.schema.ts +34 -0
- package/src/db/schema/queue.schema.ts +59 -0
- package/src/db/schema/relationships.schema.ts +42 -0
- package/src/db/schema.ts +223 -0
- package/src/db/worker-connection.ts +47 -0
- package/src/index.ts +235 -0
- package/src/mcp/CLAUDE.md +1 -0
- package/src/mcp/index.ts +1380 -0
- package/src/mcp/legacyState.ts +22 -0
- package/src/mcp/rateLimit.ts +358 -0
- package/src/mcp/resources.ts +309 -0
- package/src/mcp/results.ts +104 -0
- package/src/mcp/tools.ts +401 -0
- package/src/queues/config.ts +119 -0
- package/src/queues/index.ts +289 -0
- package/src/sdk/client.ts +225 -0
- package/src/sdk/errors.ts +266 -0
- package/src/sdk/http.ts +560 -0
- package/src/sdk/index.ts +244 -0
- package/src/sdk/resources/base.ts +65 -0
- package/src/sdk/resources/connections.ts +204 -0
- package/src/sdk/resources/documents.ts +163 -0
- package/src/sdk/resources/index.ts +10 -0
- package/src/sdk/resources/memories.ts +150 -0
- package/src/sdk/resources/search.ts +60 -0
- package/src/sdk/resources/settings.ts +36 -0
- package/src/sdk/types.ts +674 -0
- package/src/services/chunking/index.ts +451 -0
- package/src/services/chunking.service.ts +650 -0
- package/src/services/csrf.service.ts +252 -0
- package/src/services/documents.repository.ts +219 -0
- package/src/services/documents.service.ts +191 -0
- package/src/services/embedding.service.ts +404 -0
- package/src/services/extraction.service.ts +300 -0
- package/src/services/extractors/code.extractor.ts +451 -0
- package/src/services/extractors/index.ts +9 -0
- package/src/services/extractors/markdown.extractor.ts +461 -0
- package/src/services/extractors/pdf.extractor.ts +315 -0
- package/src/services/extractors/text.extractor.ts +118 -0
- package/src/services/extractors/url.extractor.ts +243 -0
- package/src/services/index.ts +235 -0
- package/src/services/ingestion.service.ts +177 -0
- package/src/services/llm/anthropic.ts +400 -0
- package/src/services/llm/base.ts +460 -0
- package/src/services/llm/contradiction-detector.service.ts +526 -0
- package/src/services/llm/heuristics.ts +148 -0
- package/src/services/llm/index.ts +309 -0
- package/src/services/llm/memory-classifier.service.ts +383 -0
- package/src/services/llm/memory-extension-detector.service.ts +523 -0
- package/src/services/llm/mock.ts +470 -0
- package/src/services/llm/openai.ts +398 -0
- package/src/services/llm/prompts.ts +438 -0
- package/src/services/llm/types.ts +373 -0
- package/src/services/memory.repository.ts +1769 -0
- package/src/services/memory.service.ts +1338 -0
- package/src/services/memory.types.ts +234 -0
- package/src/services/persistence/index.ts +295 -0
- package/src/services/pipeline.service.ts +509 -0
- package/src/services/profile.repository.ts +436 -0
- package/src/services/profile.service.ts +560 -0
- package/src/services/profile.types.ts +270 -0
- package/src/services/relationships/detector.ts +1128 -0
- package/src/services/relationships/index.ts +268 -0
- package/src/services/relationships/memory-integration.ts +459 -0
- package/src/services/relationships/strategies.ts +132 -0
- package/src/services/relationships/types.ts +370 -0
- package/src/services/search.service.ts +761 -0
- package/src/services/search.types.ts +220 -0
- package/src/services/secrets.service.ts +384 -0
- package/src/services/vectorstore/base.ts +327 -0
- package/src/services/vectorstore/index.ts +444 -0
- package/src/services/vectorstore/memory.ts +286 -0
- package/src/services/vectorstore/migration.ts +295 -0
- package/src/services/vectorstore/mock.ts +403 -0
- package/src/services/vectorstore/pgvector.ts +695 -0
- package/src/services/vectorstore/types.ts +247 -0
- package/src/startup.ts +389 -0
- package/src/types/api.types.ts +193 -0
- package/src/types/document.types.ts +103 -0
- package/src/types/index.ts +241 -0
- package/src/types/profile.base.ts +133 -0
- package/src/utils/errors.ts +447 -0
- package/src/utils/id.ts +15 -0
- package/src/utils/index.ts +101 -0
- package/src/utils/logger.ts +313 -0
- package/src/utils/sanitization.ts +501 -0
- package/src/utils/secret-validation.ts +273 -0
- package/src/utils/synonyms.ts +188 -0
- package/src/utils/validation.ts +581 -0
- package/src/workers/chunking.worker.ts +242 -0
- package/src/workers/embedding.worker.ts +358 -0
- package/src/workers/extraction.worker.ts +346 -0
- package/src/workers/indexing.worker.ts +505 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
-- Generate Test Data for HNSW Performance Testing
|
|
2
|
+
-- Creates 1K, 10K, and 100K test vectors
|
|
3
|
+
|
|
4
|
+
\echo 'Generating test data for HNSW performance benchmarking...'
|
|
5
|
+
\echo ''
|
|
6
|
+
|
|
7
|
+
-- Function to generate random vector
|
|
8
|
+
CREATE OR REPLACE FUNCTION generate_random_vector(dims INTEGER)
|
|
9
|
+
RETURNS vector AS $$
|
|
10
|
+
SELECT array_agg(random()::REAL)::vector
|
|
11
|
+
FROM generate_series(1, dims);
|
|
12
|
+
$$ LANGUAGE SQL;
|
|
13
|
+
|
|
14
|
+
-- Ensure prerequisite memory rows exist for FK constraint
|
|
15
|
+
INSERT INTO memories (id, content, container_tag)
|
|
16
|
+
SELECT
|
|
17
|
+
gen_random_uuid(),
|
|
18
|
+
'Test memory ' || i,
|
|
19
|
+
'test-data'
|
|
20
|
+
FROM generate_series(1, 1000) AS i
|
|
21
|
+
ON CONFLICT DO NOTHING;
|
|
22
|
+
|
|
23
|
+
-- Generate 1,000 vectors (1K dataset)
|
|
24
|
+
\echo 'Generating 1,000 test vectors (1K dataset)...'
|
|
25
|
+
INSERT INTO memory_embeddings (memory_id, embedding, model)
|
|
26
|
+
SELECT
|
|
27
|
+
m.id,
|
|
28
|
+
generate_random_vector(1536),
|
|
29
|
+
'text-embedding-3-small'
|
|
30
|
+
FROM (SELECT id FROM memories ORDER BY created_at DESC LIMIT 1000) m;
|
|
31
|
+
|
|
32
|
+
\echo '✓ 1K dataset complete'
|
|
33
|
+
|
|
34
|
+
-- Get count
|
|
35
|
+
SELECT COUNT(*) as total_vectors FROM memory_embeddings;
|
|
36
|
+
|
|
37
|
+
-- Analyze for query planning
|
|
38
|
+
ANALYZE memory_embeddings;
|
|
39
|
+
|
|
40
|
+
\echo ''
|
|
41
|
+
\echo 'Test data generation complete!'
|
|
42
|
+
\echo 'Ready for performance benchmarking.'
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- HNSW Index Phase 1 Comprehensive Test Suite
|
|
3
|
+
-- Task: TASK-005 from BACKLOG.md
|
|
4
|
+
-- Created: 2026-02-02
|
|
5
|
+
-- ============================================================================
|
|
6
|
+
|
|
7
|
+
\echo '╔═══════════════════════════════════════════════════════════════════════╗'
|
|
8
|
+
\echo '║ HNSW Index Phase 1 - Comprehensive Test Suite ║'
|
|
9
|
+
\echo '╚═══════════════════════════════════════════════════════════════════════╝'
|
|
10
|
+
\echo ''
|
|
11
|
+
|
|
12
|
+
-- ============================================================================
|
|
13
|
+
-- SETUP: Ensure table and index exist
|
|
14
|
+
-- ============================================================================
|
|
15
|
+
\echo '📦 SETUP: Creating table and HNSW index...'
|
|
16
|
+
|
|
17
|
+
DROP TABLE IF EXISTS memory_embeddings CASCADE;
|
|
18
|
+
|
|
19
|
+
CREATE TABLE memory_embeddings (
|
|
20
|
+
memory_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
21
|
+
embedding vector(1536) NOT NULL,
|
|
22
|
+
model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-3-small',
|
|
23
|
+
model_version VARCHAR(50),
|
|
24
|
+
normalized BOOLEAN DEFAULT TRUE,
|
|
25
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE INDEX idx_memory_embeddings_hnsw
|
|
29
|
+
ON memory_embeddings
|
|
30
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
31
|
+
WITH (m = 16, ef_construction = 64);
|
|
32
|
+
|
|
33
|
+
SET hnsw.ef_search = 100;
|
|
34
|
+
|
|
35
|
+
\echo '✓ Table and index created'
|
|
36
|
+
\echo ''
|
|
37
|
+
|
|
38
|
+
-- ============================================================================
|
|
39
|
+
-- TEST 1: Verify HNSW Index Creation
|
|
40
|
+
-- ============================================================================
|
|
41
|
+
\echo '📋 TEST 1: Verify HNSW Index Creation'
|
|
42
|
+
SELECT
|
|
43
|
+
CASE WHEN EXISTS (
|
|
44
|
+
SELECT 1 FROM pg_indexes
|
|
45
|
+
WHERE tablename = 'memory_embeddings'
|
|
46
|
+
AND indexname = 'idx_memory_embeddings_hnsw'
|
|
47
|
+
)
|
|
48
|
+
THEN ' ✓ PASS - HNSW index exists'
|
|
49
|
+
ELSE ' ✗ FAIL - HNSW index not found'
|
|
50
|
+
END as result;
|
|
51
|
+
\echo ''
|
|
52
|
+
|
|
53
|
+
-- ============================================================================
|
|
54
|
+
-- TEST 2: Verify HNSW Access Method
|
|
55
|
+
-- ============================================================================
|
|
56
|
+
\echo '📋 TEST 2: Verify HNSW Access Method'
|
|
57
|
+
SELECT
|
|
58
|
+
' ✓ PASS - Access Method: ' || am.amname as result
|
|
59
|
+
FROM pg_class c
|
|
60
|
+
JOIN pg_am am ON c.relam = am.oid
|
|
61
|
+
WHERE c.relname = 'idx_memory_embeddings_hnsw';
|
|
62
|
+
\echo ''
|
|
63
|
+
|
|
64
|
+
-- ============================================================================
|
|
65
|
+
-- TEST 3: Verify HNSW Parameters (m=16, ef_construction=64)
|
|
66
|
+
-- ============================================================================
|
|
67
|
+
\echo '📋 TEST 3: Verify HNSW Parameters'
|
|
68
|
+
\echo ' Expected: m=16, ef_construction=64'
|
|
69
|
+
\d idx_memory_embeddings_hnsw
|
|
70
|
+
\echo ''
|
|
71
|
+
|
|
72
|
+
-- ============================================================================
|
|
73
|
+
-- TEST 4: Verify ef_search Configuration
|
|
74
|
+
-- ============================================================================
|
|
75
|
+
\echo '📋 TEST 4: Verify ef_search Configuration'
|
|
76
|
+
\echo ' Expected: 100 (balanced mode, ~99% recall)'
|
|
77
|
+
SHOW hnsw.ef_search;
|
|
78
|
+
\echo ''
|
|
79
|
+
|
|
80
|
+
-- ============================================================================
|
|
81
|
+
-- TEST 5: Helper Functions
|
|
82
|
+
-- ============================================================================
|
|
83
|
+
\echo '📋 TEST 5: Create and Test Helper Functions'
|
|
84
|
+
|
|
85
|
+
CREATE OR REPLACE FUNCTION set_hnsw_search_quality(quality_level TEXT DEFAULT 'balanced')
|
|
86
|
+
RETURNS TEXT AS $$
|
|
87
|
+
BEGIN
|
|
88
|
+
CASE quality_level
|
|
89
|
+
WHEN 'fast' THEN
|
|
90
|
+
EXECUTE 'SET hnsw.ef_search = 40';
|
|
91
|
+
RETURN ' ✓ PASS - Set to FAST (ef_search=40, ~95% recall)';
|
|
92
|
+
WHEN 'balanced' THEN
|
|
93
|
+
EXECUTE 'SET hnsw.ef_search = 100';
|
|
94
|
+
RETURN ' ✓ PASS - Set to BALANCED (ef_search=100, ~99% recall)';
|
|
95
|
+
WHEN 'accurate' THEN
|
|
96
|
+
EXECUTE 'SET hnsw.ef_search = 200';
|
|
97
|
+
RETURN ' ✓ PASS - Set to ACCURATE (ef_search=200, ~99.5%+ recall)';
|
|
98
|
+
ELSE
|
|
99
|
+
RAISE EXCEPTION 'Invalid quality_level. Use: fast, balanced, or accurate';
|
|
100
|
+
END CASE;
|
|
101
|
+
END;
|
|
102
|
+
$$ LANGUAGE plpgsql;
|
|
103
|
+
|
|
104
|
+
SELECT set_hnsw_search_quality('fast');
|
|
105
|
+
SELECT set_hnsw_search_quality('balanced');
|
|
106
|
+
SELECT set_hnsw_search_quality('accurate');
|
|
107
|
+
\echo ''
|
|
108
|
+
|
|
109
|
+
-- ============================================================================
|
|
110
|
+
-- TEST 6: Generate Test Data (1K vectors)
|
|
111
|
+
-- ============================================================================
|
|
112
|
+
\echo '📋 TEST 6: Generate Test Data (1,000 vectors)'
|
|
113
|
+
|
|
114
|
+
CREATE OR REPLACE FUNCTION generate_random_vector(dims INTEGER)
|
|
115
|
+
RETURNS vector AS $$
|
|
116
|
+
SELECT array_agg(random()::REAL)::vector
|
|
117
|
+
FROM generate_series(1, dims);
|
|
118
|
+
$$ LANGUAGE SQL;
|
|
119
|
+
|
|
120
|
+
INSERT INTO memory_embeddings (memory_id, embedding)
|
|
121
|
+
SELECT
|
|
122
|
+
gen_random_uuid(),
|
|
123
|
+
generate_random_vector(1536)
|
|
124
|
+
FROM generate_series(1, 1000);
|
|
125
|
+
|
|
126
|
+
ANALYZE memory_embeddings;
|
|
127
|
+
|
|
128
|
+
SELECT ' ✓ PASS - Generated ' || COUNT(*) || ' test vectors' as result
|
|
129
|
+
FROM memory_embeddings;
|
|
130
|
+
\echo ''
|
|
131
|
+
|
|
132
|
+
-- ============================================================================
|
|
133
|
+
-- TEST 7: Performance Benchmark (1K vectors)
|
|
134
|
+
-- ============================================================================
|
|
135
|
+
\echo '📋 TEST 7: Performance Benchmark (Target: < 10ms for 1K vectors)'
|
|
136
|
+
|
|
137
|
+
CREATE OR REPLACE FUNCTION run_single_benchmark()
|
|
138
|
+
RETURNS TABLE (
|
|
139
|
+
execution_time_ms NUMERIC,
|
|
140
|
+
results_count INTEGER
|
|
141
|
+
) AS $$
|
|
142
|
+
DECLARE
|
|
143
|
+
start_time TIMESTAMPTZ;
|
|
144
|
+
exec_time NUMERIC;
|
|
145
|
+
res_count INTEGER;
|
|
146
|
+
sample_vec vector(1536);
|
|
147
|
+
BEGIN
|
|
148
|
+
sample_vec := (SELECT array_agg(random()::REAL)::vector FROM generate_series(1, 1536));
|
|
149
|
+
|
|
150
|
+
start_time := clock_timestamp();
|
|
151
|
+
|
|
152
|
+
SELECT COUNT(*) INTO res_count
|
|
153
|
+
FROM (SELECT memory_id FROM memory_embeddings ORDER BY embedding <=> sample_vec LIMIT 10) t;
|
|
154
|
+
|
|
155
|
+
exec_time := EXTRACT(MILLISECONDS FROM (clock_timestamp() - start_time));
|
|
156
|
+
|
|
157
|
+
RETURN QUERY SELECT exec_time, res_count;
|
|
158
|
+
END;
|
|
159
|
+
$$ LANGUAGE plpgsql;
|
|
160
|
+
|
|
161
|
+
-- Run 10 benchmark queries
|
|
162
|
+
SELECT
|
|
163
|
+
' Query ' || row_number() OVER () || ': ' ||
|
|
164
|
+
ROUND(execution_time_ms, 2) || ' ms - ' ||
|
|
165
|
+
CASE
|
|
166
|
+
WHEN execution_time_ms < 10 THEN '✓ EXCELLENT'
|
|
167
|
+
WHEN execution_time_ms < 100 THEN '✓ PASS'
|
|
168
|
+
ELSE '⚠ SLOW'
|
|
169
|
+
END as result
|
|
170
|
+
FROM (SELECT * FROM run_single_benchmark() UNION ALL
|
|
171
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
172
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
173
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
174
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
175
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
176
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
177
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
178
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
179
|
+
SELECT * FROM run_single_benchmark()) benchmarks;
|
|
180
|
+
|
|
181
|
+
\echo ''
|
|
182
|
+
\echo '📊 Benchmark Summary:'
|
|
183
|
+
WITH bench AS (
|
|
184
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
185
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
186
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
187
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
188
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
189
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
190
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
191
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
192
|
+
SELECT * FROM run_single_benchmark() UNION ALL
|
|
193
|
+
SELECT * FROM run_single_benchmark()
|
|
194
|
+
)
|
|
195
|
+
SELECT
|
|
196
|
+
' Average: ' || ROUND(AVG(execution_time_ms), 2) || ' ms' as avg,
|
|
197
|
+
' Min: ' || ROUND(MIN(execution_time_ms), 2) || ' ms' as min,
|
|
198
|
+
' Max: ' || ROUND(MAX(execution_time_ms), 2) || ' ms' as max
|
|
199
|
+
FROM bench;
|
|
200
|
+
|
|
201
|
+
\echo ''
|
|
202
|
+
\echo '╔═══════════════════════════════════════════════════════════════════════╗'
|
|
203
|
+
\echo '║ Test Suite Complete ║'
|
|
204
|
+
\echo '╚═══════════════════════════════════════════════════════════════════════╝'
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
###############################################################################
|
|
4
|
+
# PostgreSQL Migration Runner for Supermemory
|
|
5
|
+
# Description: Runs all database migrations in order with error handling
|
|
6
|
+
# Created: 2026-02-02
|
|
7
|
+
# Related: TASK-005 (HNSW Index), TASK-002 (PostgreSQL Schema)
|
|
8
|
+
###############################################################################
|
|
9
|
+
|
|
10
|
+
set -e # Exit on error
|
|
11
|
+
|
|
12
|
+
# Colors for output
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
GREEN='\033[0;32m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
BLUE='\033[0;34m'
|
|
17
|
+
NC='\033[0m' # No Color
|
|
18
|
+
|
|
19
|
+
# Migration directory
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
MIGRATIONS_DIR="$SCRIPT_DIR"
|
|
22
|
+
|
|
23
|
+
# Load .env from project root if available
|
|
24
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
25
|
+
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
26
|
+
set -a
|
|
27
|
+
source "$PROJECT_ROOT/.env"
|
|
28
|
+
set +a
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Database URL from environment or default
|
|
32
|
+
DATABASE_URL="${DATABASE_URL:-postgresql://supermemory:supermemory_secret@localhost:15432/supermemory}"
|
|
33
|
+
|
|
34
|
+
# Check if DATABASE_URL is set
|
|
35
|
+
if [ -z "$DATABASE_URL" ]; then
|
|
36
|
+
echo -e "${RED}Error: DATABASE_URL not set${NC}"
|
|
37
|
+
echo "Usage: DATABASE_URL=postgresql://user:pass@host:port/db $0"
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Function to print colored messages
|
|
42
|
+
print_info() {
|
|
43
|
+
echo -e "${BLUE}[INFO]${NC} $1"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
print_success() {
|
|
47
|
+
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
print_warning() {
|
|
51
|
+
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
print_error() {
|
|
55
|
+
echo -e "${RED}[ERROR]${NC} $1"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Function to check PostgreSQL connection
|
|
59
|
+
check_connection() {
|
|
60
|
+
print_info "Checking database connection..."
|
|
61
|
+
|
|
62
|
+
if ! psql "$DATABASE_URL" -c "SELECT 1;" > /dev/null 2>&1; then
|
|
63
|
+
print_error "Cannot connect to database: $DATABASE_URL"
|
|
64
|
+
return 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
print_success "Database connection OK"
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Function to check PostgreSQL version
|
|
72
|
+
check_postgres_version() {
|
|
73
|
+
print_info "Checking PostgreSQL version..."
|
|
74
|
+
|
|
75
|
+
local version=$(psql "$DATABASE_URL" -t -c "SHOW server_version;" | awk '{print $1}' | cut -d. -f1)
|
|
76
|
+
|
|
77
|
+
if [ "$version" -lt 12 ]; then
|
|
78
|
+
print_error "PostgreSQL version $version is too old. Requires 12+, recommended 15+"
|
|
79
|
+
return 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
print_success "PostgreSQL version $version OK"
|
|
83
|
+
return 0
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Function to check if pgvector is available
|
|
87
|
+
check_pgvector_available() {
|
|
88
|
+
print_info "Checking if pgvector is available..."
|
|
89
|
+
|
|
90
|
+
if ! psql "$DATABASE_URL" -t -c "SELECT * FROM pg_available_extensions WHERE name='vector';" | grep -q vector; then
|
|
91
|
+
print_error "pgvector extension is not available"
|
|
92
|
+
print_info "Install instructions:"
|
|
93
|
+
print_info " macOS: brew install pgvector"
|
|
94
|
+
print_info " Ubuntu: sudo apt install postgresql-15-pgvector"
|
|
95
|
+
print_info " From source: https://github.com/pgvector/pgvector"
|
|
96
|
+
return 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
print_success "pgvector extension is available"
|
|
100
|
+
return 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Function to run a migration file
|
|
104
|
+
run_migration() {
|
|
105
|
+
local migration_file="$1"
|
|
106
|
+
local migration_name=$(basename "$migration_file")
|
|
107
|
+
|
|
108
|
+
print_info "Running migration: $migration_name"
|
|
109
|
+
|
|
110
|
+
if ! psql "$DATABASE_URL" -f "$migration_file" > /dev/null 2>&1; then
|
|
111
|
+
print_error "Migration failed: $migration_name"
|
|
112
|
+
print_info "Run with verbose mode to see details:"
|
|
113
|
+
print_info " psql $DATABASE_URL -f $migration_file"
|
|
114
|
+
return 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
print_success "Migration completed: $migration_name"
|
|
118
|
+
return 0
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Function to run all migrations
|
|
122
|
+
run_all_migrations() {
|
|
123
|
+
local migrations=(
|
|
124
|
+
"001_create_pgvector_extension.sql"
|
|
125
|
+
"003_create_hnsw_index.sql"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
print_info "Starting migration process..."
|
|
129
|
+
echo ""
|
|
130
|
+
|
|
131
|
+
for migration in "${migrations[@]}"; do
|
|
132
|
+
local migration_path="$MIGRATIONS_DIR/$migration"
|
|
133
|
+
|
|
134
|
+
if [ ! -f "$migration_path" ]; then
|
|
135
|
+
print_error "Migration file not found: $migration"
|
|
136
|
+
return 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if ! run_migration "$migration_path"; then
|
|
140
|
+
print_error "Migration process aborted"
|
|
141
|
+
return 1
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
echo ""
|
|
145
|
+
done
|
|
146
|
+
|
|
147
|
+
print_success "All migrations completed successfully!"
|
|
148
|
+
return 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Function to run tests
|
|
152
|
+
run_tests() {
|
|
153
|
+
local test_file="$MIGRATIONS_DIR/test_hnsw_index.sql"
|
|
154
|
+
|
|
155
|
+
print_info "Running test suite..."
|
|
156
|
+
echo ""
|
|
157
|
+
|
|
158
|
+
if [ ! -f "$test_file" ]; then
|
|
159
|
+
print_warning "Test file not found: $test_file"
|
|
160
|
+
return 1
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
if ! psql "$DATABASE_URL" -f "$test_file"; then
|
|
164
|
+
print_warning "Some tests may have warnings (this is normal without data)"
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
print_success "Tests completed"
|
|
169
|
+
return 0
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# Function to show migration status
|
|
173
|
+
show_status() {
|
|
174
|
+
print_info "Checking migration status..."
|
|
175
|
+
echo ""
|
|
176
|
+
|
|
177
|
+
# Check pgvector extension
|
|
178
|
+
if psql "$DATABASE_URL" -t -c "SELECT 1 FROM pg_extension WHERE extname='vector';" | grep -q 1; then
|
|
179
|
+
print_success "✓ pgvector extension installed"
|
|
180
|
+
else
|
|
181
|
+
print_warning "✗ pgvector extension not installed"
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
# Check memory_embeddings table
|
|
185
|
+
if psql "$DATABASE_URL" -t -c "SELECT 1 FROM pg_tables WHERE tablename='memory_embeddings';" | grep -q 1; then
|
|
186
|
+
print_success "✓ memory_embeddings table exists"
|
|
187
|
+
|
|
188
|
+
# Get row count
|
|
189
|
+
local row_count=$(psql "$DATABASE_URL" -t -c "SELECT COUNT(*) FROM memory_embeddings;")
|
|
190
|
+
print_info " Rows: $(echo $row_count | xargs)"
|
|
191
|
+
else
|
|
192
|
+
print_warning "✗ memory_embeddings table not found"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Check HNSW index
|
|
196
|
+
if psql "$DATABASE_URL" -t -c "SELECT 1 FROM pg_indexes WHERE indexname='idx_memory_embeddings_hnsw';" | grep -q 1; then
|
|
197
|
+
print_success "✓ HNSW index exists"
|
|
198
|
+
|
|
199
|
+
# Get index size
|
|
200
|
+
local index_size=$(psql "$DATABASE_URL" -t -c "SELECT pg_size_pretty(pg_relation_size('idx_memory_embeddings_hnsw'));")
|
|
201
|
+
print_info " Size: $(echo $index_size | xargs)"
|
|
202
|
+
|
|
203
|
+
# Get ef_search setting
|
|
204
|
+
local ef_search=$(psql "$DATABASE_URL" -t -c "SHOW hnsw.ef_search;" 2>/dev/null || echo "not set")
|
|
205
|
+
print_info " ef_search: $(echo $ef_search | xargs)"
|
|
206
|
+
else
|
|
207
|
+
print_warning "✗ HNSW index not found"
|
|
208
|
+
fi
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Function to display usage
|
|
212
|
+
show_usage() {
|
|
213
|
+
cat << EOF
|
|
214
|
+
PostgreSQL Migration Runner for Supermemory
|
|
215
|
+
|
|
216
|
+
Usage: $0 [OPTIONS]
|
|
217
|
+
|
|
218
|
+
Options:
|
|
219
|
+
run Run all migrations (default)
|
|
220
|
+
test Run test suite
|
|
221
|
+
status Show migration status
|
|
222
|
+
help Show this help message
|
|
223
|
+
|
|
224
|
+
Environment Variables:
|
|
225
|
+
DATABASE_URL PostgreSQL connection string (required)
|
|
226
|
+
Format: postgresql://user:password@host:port/database
|
|
227
|
+
|
|
228
|
+
Examples:
|
|
229
|
+
# Run migrations
|
|
230
|
+
DATABASE_URL=postgresql://localhost/supermemory $0 run
|
|
231
|
+
|
|
232
|
+
# Check status
|
|
233
|
+
DATABASE_URL=postgresql://localhost/supermemory $0 status
|
|
234
|
+
|
|
235
|
+
# Run tests
|
|
236
|
+
DATABASE_URL=postgresql://localhost/supermemory $0 test
|
|
237
|
+
|
|
238
|
+
EOF
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Main execution
|
|
242
|
+
main() {
|
|
243
|
+
local command="${1:-run}"
|
|
244
|
+
|
|
245
|
+
echo ""
|
|
246
|
+
echo "╔════════════════════════════════════════════════════════════╗"
|
|
247
|
+
echo "║ PostgreSQL Migration Runner - Supermemory ║"
|
|
248
|
+
echo "╚════════════════════════════════════════════════════════════╝"
|
|
249
|
+
echo ""
|
|
250
|
+
|
|
251
|
+
case "$command" in
|
|
252
|
+
run)
|
|
253
|
+
check_connection || exit 1
|
|
254
|
+
check_postgres_version || exit 1
|
|
255
|
+
check_pgvector_available || exit 1
|
|
256
|
+
run_all_migrations || exit 1
|
|
257
|
+
echo ""
|
|
258
|
+
print_info "Run tests with: $0 test"
|
|
259
|
+
;;
|
|
260
|
+
test)
|
|
261
|
+
check_connection || exit 1
|
|
262
|
+
run_tests
|
|
263
|
+
;;
|
|
264
|
+
status)
|
|
265
|
+
check_connection || exit 1
|
|
266
|
+
show_status
|
|
267
|
+
;;
|
|
268
|
+
help|--help|-h)
|
|
269
|
+
show_usage
|
|
270
|
+
exit 0
|
|
271
|
+
;;
|
|
272
|
+
*)
|
|
273
|
+
print_error "Unknown command: $command"
|
|
274
|
+
echo ""
|
|
275
|
+
show_usage
|
|
276
|
+
exit 1
|
|
277
|
+
;;
|
|
278
|
+
esac
|
|
279
|
+
|
|
280
|
+
echo ""
|
|
281
|
+
print_success "Done!"
|
|
282
|
+
echo ""
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Run main function with all arguments
|
|
286
|
+
main "$@"
|