@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.
Files changed (156) hide show
  1. package/.env.example +57 -0
  2. package/README.md +374 -0
  3. package/dist/index.js +189 -0
  4. package/dist/mcp/index.js +1132 -0
  5. package/docker-compose.prod.yml +91 -0
  6. package/docker-compose.yml +358 -0
  7. package/drizzle/0000_dapper_the_professor.sql +159 -0
  8. package/drizzle/0001_api_keys.sql +51 -0
  9. package/drizzle/meta/0000_snapshot.json +1532 -0
  10. package/drizzle/meta/_journal.json +13 -0
  11. package/drizzle.config.ts +20 -0
  12. package/package.json +114 -0
  13. package/scripts/add-extraction-job.ts +122 -0
  14. package/scripts/benchmark-pgvector.ts +122 -0
  15. package/scripts/bootstrap.sh +209 -0
  16. package/scripts/check-runtime-pack.ts +111 -0
  17. package/scripts/claude-mcp-config.ts +336 -0
  18. package/scripts/docker-entrypoint.sh +183 -0
  19. package/scripts/doctor.ts +377 -0
  20. package/scripts/init-db.sql +33 -0
  21. package/scripts/install.sh +1110 -0
  22. package/scripts/mcp-setup.ts +271 -0
  23. package/scripts/migrations/001_create_pgvector_extension.sql +31 -0
  24. package/scripts/migrations/002_create_memory_embeddings_table.sql +75 -0
  25. package/scripts/migrations/003_create_hnsw_index.sql +94 -0
  26. package/scripts/migrations/004_create_memory_embeddings_standalone.sql +70 -0
  27. package/scripts/migrations/005_create_chunks_table.sql +95 -0
  28. package/scripts/migrations/006_create_processing_queue.sql +45 -0
  29. package/scripts/migrations/generate_test_data.sql +42 -0
  30. package/scripts/migrations/phase1_comprehensive_test.sql +204 -0
  31. package/scripts/migrations/run_migrations.sh +286 -0
  32. package/scripts/migrations/test_hnsw_index.sql +255 -0
  33. package/scripts/pre-commit-secrets +282 -0
  34. package/scripts/run-extraction-worker.ts +46 -0
  35. package/scripts/run-phase1-tests.sh +291 -0
  36. package/scripts/setup.ts +222 -0
  37. package/scripts/smoke-install.sh +12 -0
  38. package/scripts/test-health-endpoint.sh +328 -0
  39. package/src/api/index.ts +2 -0
  40. package/src/api/middleware/auth.ts +80 -0
  41. package/src/api/middleware/csrf.ts +308 -0
  42. package/src/api/middleware/errorHandler.ts +166 -0
  43. package/src/api/middleware/rateLimit.ts +360 -0
  44. package/src/api/middleware/validation.ts +514 -0
  45. package/src/api/routes/documents.ts +286 -0
  46. package/src/api/routes/profiles.ts +237 -0
  47. package/src/api/routes/search.ts +71 -0
  48. package/src/api/stores/index.ts +58 -0
  49. package/src/config/bootstrap-env.ts +3 -0
  50. package/src/config/env.ts +71 -0
  51. package/src/config/feature-flags.ts +25 -0
  52. package/src/config/index.ts +140 -0
  53. package/src/config/secrets.config.ts +291 -0
  54. package/src/db/client.ts +92 -0
  55. package/src/db/index.ts +73 -0
  56. package/src/db/postgres.ts +72 -0
  57. package/src/db/schema/chunks.schema.ts +31 -0
  58. package/src/db/schema/containers.schema.ts +46 -0
  59. package/src/db/schema/documents.schema.ts +49 -0
  60. package/src/db/schema/embeddings.schema.ts +32 -0
  61. package/src/db/schema/index.ts +11 -0
  62. package/src/db/schema/memories.schema.ts +72 -0
  63. package/src/db/schema/profiles.schema.ts +34 -0
  64. package/src/db/schema/queue.schema.ts +59 -0
  65. package/src/db/schema/relationships.schema.ts +42 -0
  66. package/src/db/schema.ts +223 -0
  67. package/src/db/worker-connection.ts +47 -0
  68. package/src/index.ts +235 -0
  69. package/src/mcp/CLAUDE.md +1 -0
  70. package/src/mcp/index.ts +1380 -0
  71. package/src/mcp/legacyState.ts +22 -0
  72. package/src/mcp/rateLimit.ts +358 -0
  73. package/src/mcp/resources.ts +309 -0
  74. package/src/mcp/results.ts +104 -0
  75. package/src/mcp/tools.ts +401 -0
  76. package/src/queues/config.ts +119 -0
  77. package/src/queues/index.ts +289 -0
  78. package/src/sdk/client.ts +225 -0
  79. package/src/sdk/errors.ts +266 -0
  80. package/src/sdk/http.ts +560 -0
  81. package/src/sdk/index.ts +244 -0
  82. package/src/sdk/resources/base.ts +65 -0
  83. package/src/sdk/resources/connections.ts +204 -0
  84. package/src/sdk/resources/documents.ts +163 -0
  85. package/src/sdk/resources/index.ts +10 -0
  86. package/src/sdk/resources/memories.ts +150 -0
  87. package/src/sdk/resources/search.ts +60 -0
  88. package/src/sdk/resources/settings.ts +36 -0
  89. package/src/sdk/types.ts +674 -0
  90. package/src/services/chunking/index.ts +451 -0
  91. package/src/services/chunking.service.ts +650 -0
  92. package/src/services/csrf.service.ts +252 -0
  93. package/src/services/documents.repository.ts +219 -0
  94. package/src/services/documents.service.ts +191 -0
  95. package/src/services/embedding.service.ts +404 -0
  96. package/src/services/extraction.service.ts +300 -0
  97. package/src/services/extractors/code.extractor.ts +451 -0
  98. package/src/services/extractors/index.ts +9 -0
  99. package/src/services/extractors/markdown.extractor.ts +461 -0
  100. package/src/services/extractors/pdf.extractor.ts +315 -0
  101. package/src/services/extractors/text.extractor.ts +118 -0
  102. package/src/services/extractors/url.extractor.ts +243 -0
  103. package/src/services/index.ts +235 -0
  104. package/src/services/ingestion.service.ts +177 -0
  105. package/src/services/llm/anthropic.ts +400 -0
  106. package/src/services/llm/base.ts +460 -0
  107. package/src/services/llm/contradiction-detector.service.ts +526 -0
  108. package/src/services/llm/heuristics.ts +148 -0
  109. package/src/services/llm/index.ts +309 -0
  110. package/src/services/llm/memory-classifier.service.ts +383 -0
  111. package/src/services/llm/memory-extension-detector.service.ts +523 -0
  112. package/src/services/llm/mock.ts +470 -0
  113. package/src/services/llm/openai.ts +398 -0
  114. package/src/services/llm/prompts.ts +438 -0
  115. package/src/services/llm/types.ts +373 -0
  116. package/src/services/memory.repository.ts +1769 -0
  117. package/src/services/memory.service.ts +1338 -0
  118. package/src/services/memory.types.ts +234 -0
  119. package/src/services/persistence/index.ts +295 -0
  120. package/src/services/pipeline.service.ts +509 -0
  121. package/src/services/profile.repository.ts +436 -0
  122. package/src/services/profile.service.ts +560 -0
  123. package/src/services/profile.types.ts +270 -0
  124. package/src/services/relationships/detector.ts +1128 -0
  125. package/src/services/relationships/index.ts +268 -0
  126. package/src/services/relationships/memory-integration.ts +459 -0
  127. package/src/services/relationships/strategies.ts +132 -0
  128. package/src/services/relationships/types.ts +370 -0
  129. package/src/services/search.service.ts +761 -0
  130. package/src/services/search.types.ts +220 -0
  131. package/src/services/secrets.service.ts +384 -0
  132. package/src/services/vectorstore/base.ts +327 -0
  133. package/src/services/vectorstore/index.ts +444 -0
  134. package/src/services/vectorstore/memory.ts +286 -0
  135. package/src/services/vectorstore/migration.ts +295 -0
  136. package/src/services/vectorstore/mock.ts +403 -0
  137. package/src/services/vectorstore/pgvector.ts +695 -0
  138. package/src/services/vectorstore/types.ts +247 -0
  139. package/src/startup.ts +389 -0
  140. package/src/types/api.types.ts +193 -0
  141. package/src/types/document.types.ts +103 -0
  142. package/src/types/index.ts +241 -0
  143. package/src/types/profile.base.ts +133 -0
  144. package/src/utils/errors.ts +447 -0
  145. package/src/utils/id.ts +15 -0
  146. package/src/utils/index.ts +101 -0
  147. package/src/utils/logger.ts +313 -0
  148. package/src/utils/sanitization.ts +501 -0
  149. package/src/utils/secret-validation.ts +273 -0
  150. package/src/utils/synonyms.ts +188 -0
  151. package/src/utils/validation.ts +581 -0
  152. package/src/workers/chunking.worker.ts +242 -0
  153. package/src/workers/embedding.worker.ts +358 -0
  154. package/src/workers/extraction.worker.ts +346 -0
  155. package/src/workers/indexing.worker.ts +505 -0
  156. 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 "$@"