@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,377 @@
1
+ #!/usr/bin/env tsx
2
+ import { existsSync } from 'node:fs';
3
+ import { readFile } from 'node:fs/promises';
4
+ import pkg from 'pg';
5
+ import Redis from 'ioredis';
6
+ import { loadEnvFile } from '../src/config/env.js';
7
+ import { findClaudeMcpRegistrations } from './claude-mcp-config.js';
8
+
9
+ const { Client } = pkg;
10
+ type RedisLike = {
11
+ on(event: 'error', listener: (error: unknown) => void): void;
12
+ ping(): Promise<string>;
13
+ quit(): Promise<'OK' | void>;
14
+ };
15
+
16
+ type CheckResult = {
17
+ ok: boolean;
18
+ level: 'error' | 'warn' | 'info';
19
+ message: string;
20
+ };
21
+
22
+ type DoctorMode = 'agent' | 'api' | 'full';
23
+
24
+ function isRecord(value: unknown): value is Record<string, unknown> {
25
+ return !!value && typeof value === 'object' && !Array.isArray(value);
26
+ }
27
+
28
+ function parseEnv(raw: string): Record<string, string> {
29
+ const env: Record<string, string> = {};
30
+ for (const line of raw.split('\n')) {
31
+ const trimmed = line.trim();
32
+ if (!trimmed || trimmed.startsWith('#')) continue;
33
+ const idx = trimmed.indexOf('=');
34
+ if (idx <= 0) continue;
35
+ const key = trimmed.slice(0, idx).trim();
36
+ const value = trimmed.slice(idx + 1).trim();
37
+ env[key] = value;
38
+ }
39
+ return env;
40
+ }
41
+
42
+ function printResult(result: CheckResult): void {
43
+ const levelPrefix: Record<CheckResult['level'], string> = {
44
+ error: 'FAIL',
45
+ warn: 'WARN',
46
+ info: 'OK',
47
+ };
48
+ const prefix = levelPrefix[result.level];
49
+ console.log(`[${prefix}] ${result.message}`);
50
+ }
51
+
52
+ function validateMode(mode: string): DoctorMode {
53
+ if (mode === 'agent' || mode === 'api' || mode === 'full') {
54
+ return mode;
55
+ }
56
+
57
+ throw new Error(`Invalid mode: ${mode} (expected: agent, api, or full)`);
58
+ }
59
+
60
+ function getApiHealthUrl(env: Record<string, string>): string {
61
+ const apiHostPort = env.API_HOST_PORT || env.API_PORT || '13000';
62
+ return `http://127.0.0.1:${apiHostPort}/health`;
63
+ }
64
+
65
+ async function checkApiHealth(env: Record<string, string>): Promise<CheckResult> {
66
+ const healthUrl = getApiHealthUrl(env);
67
+ let lastError = 'unknown error';
68
+
69
+ for (let attempt = 1; attempt <= 10; attempt += 1) {
70
+ try {
71
+ const response = await fetch(healthUrl, { signal: AbortSignal.timeout(5000) });
72
+ if (response.ok) {
73
+ return { ok: true, level: 'info', message: `API health endpoint reachable: ${healthUrl}` };
74
+ }
75
+
76
+ lastError = `returned HTTP ${response.status}`;
77
+ } catch (error) {
78
+ lastError = error instanceof Error ? error.message : String(error);
79
+ }
80
+
81
+ if (attempt < 10) {
82
+ await new Promise((resolve) => setTimeout(resolve, 1500));
83
+ }
84
+ }
85
+
86
+ return {
87
+ ok: false,
88
+ level: 'error',
89
+ message: `API health check failed: ${healthUrl} (${lastError})`,
90
+ };
91
+ }
92
+
93
+ function parseArgs(): { envFile?: string; mode: DoctorMode } {
94
+ const args = process.argv.slice(2);
95
+ let envFile: string | undefined;
96
+ let mode: DoctorMode = 'agent';
97
+
98
+ for (let index = 0; index < args.length; index += 1) {
99
+ const arg = args[index];
100
+
101
+ if (arg === '--env-file') {
102
+ const value = args[index + 1];
103
+ if (!value) {
104
+ throw new Error('--env-file requires a value');
105
+ }
106
+ envFile = value;
107
+ index += 1;
108
+ continue;
109
+ }
110
+
111
+ if (arg.startsWith('--env-file=')) {
112
+ envFile = arg.slice('--env-file='.length);
113
+ continue;
114
+ }
115
+
116
+ if (arg === '--mode') {
117
+ const value = args[index + 1];
118
+ if (!value) {
119
+ throw new Error('--mode requires a value');
120
+ }
121
+ mode = validateMode(value.toLowerCase());
122
+ index += 1;
123
+ continue;
124
+ }
125
+
126
+ if (arg.startsWith('--mode=')) {
127
+ mode = validateMode(arg.slice('--mode='.length).toLowerCase());
128
+ continue;
129
+ }
130
+
131
+ throw new Error(`Unknown argument: ${arg}`);
132
+ }
133
+
134
+ return { envFile, mode };
135
+ }
136
+
137
+ async function run(): Promise<void> {
138
+ const { envFile, mode } = parseArgs();
139
+ const results: CheckResult[] = [];
140
+ const envResolution = loadEnvFile({ cliEnvFile: envFile });
141
+
142
+ if (!envResolution.exists || !existsSync(envResolution.path)) {
143
+ results.push({
144
+ ok: false,
145
+ level: 'error',
146
+ message: `${envResolution.path} is missing (run \`npm run setup\` first or pass \`--env-file\`)`,
147
+ });
148
+ printResult(results[0]);
149
+ process.exit(1);
150
+ }
151
+
152
+ results.push({
153
+ ok: true,
154
+ level: 'info',
155
+ message: `Using env file: ${envResolution.path}`,
156
+ });
157
+ results.push({
158
+ ok: true,
159
+ level: 'info',
160
+ message: `Doctor mode: ${mode}`,
161
+ });
162
+
163
+ const env = parseEnv(await readFile(envResolution.path, 'utf-8'));
164
+ const openaiKey = env.OPENAI_API_KEY || process.env.OPENAI_API_KEY || '';
165
+ const anthropicKey = env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || '';
166
+ const llmProvider = env.LLM_PROVIDER || process.env.LLM_PROVIDER || '';
167
+ const hasOpenAIKey = !!openaiKey && !openaiKey.startsWith('sk-your-');
168
+ const hasAnthropicKey = !!anthropicKey && anthropicKey !== 'anthropic-your-api-key-here';
169
+
170
+ const databaseUrl = env.DATABASE_URL || process.env.DATABASE_URL || '';
171
+ const hasValidDatabaseUrl =
172
+ databaseUrl.startsWith('postgres://') || databaseUrl.startsWith('postgresql://');
173
+ if (!databaseUrl) {
174
+ results.push({ ok: false, level: 'error', message: 'DATABASE_URL is not set' });
175
+ } else if (!hasValidDatabaseUrl) {
176
+ results.push({
177
+ ok: false,
178
+ level: 'error',
179
+ message: 'DATABASE_URL must use postgres:// or postgresql://',
180
+ });
181
+ } else {
182
+ results.push({ ok: true, level: 'info', message: 'DATABASE_URL format is valid' });
183
+ }
184
+
185
+ const authEnabled = (env.AUTH_ENABLED || 'false') === 'true';
186
+ if (authEnabled) {
187
+ const token = env.AUTH_TOKEN || '';
188
+ if (token.length < 16) {
189
+ results.push({
190
+ ok: false,
191
+ level: 'error',
192
+ message: 'AUTH_ENABLED=true requires AUTH_TOKEN with at least 16 characters',
193
+ });
194
+ } else {
195
+ results.push({ ok: true, level: 'info', message: 'AUTH configuration is valid' });
196
+ }
197
+ } else {
198
+ results.push({
199
+ ok: true,
200
+ level: 'info',
201
+ message: 'REST API auth disabled (AUTH_ENABLED=false)',
202
+ });
203
+ }
204
+
205
+ const apiPort = Number(env.API_PORT || 3000);
206
+ if (!Number.isInteger(apiPort) || apiPort <= 0 || apiPort > 65535) {
207
+ results.push({ ok: false, level: 'error', message: 'API_PORT must be a valid TCP port' });
208
+ } else {
209
+ results.push({ ok: true, level: 'info', message: `API_PORT=${apiPort}` });
210
+ }
211
+
212
+ if (hasValidDatabaseUrl) {
213
+ const client = new Client({ connectionString: databaseUrl });
214
+ try {
215
+ await client.connect();
216
+ await client.query('SELECT 1');
217
+ results.push({ ok: true, level: 'info', message: 'PostgreSQL connection successful' });
218
+ } catch (error) {
219
+ results.push({
220
+ ok: false,
221
+ level: 'error',
222
+ message: `PostgreSQL connection failed: ${
223
+ error instanceof Error ? error.message : String(error)
224
+ }`,
225
+ });
226
+ } finally {
227
+ await client.end().catch(() => undefined);
228
+ }
229
+ }
230
+
231
+ const redisUrl = env.REDIS_URL || process.env.REDIS_URL || '';
232
+ if (!redisUrl) {
233
+ results.push({
234
+ ok: true,
235
+ level: 'warn',
236
+ message: 'REDIS_URL is unset; queue workers disabled, inline ingestion fallback will be used',
237
+ });
238
+ } else {
239
+ const RedisClientConstructor = Redis as unknown as new (
240
+ url: string,
241
+ options: { maxRetriesPerRequest: number }
242
+ ) => RedisLike;
243
+ const redis = new RedisClientConstructor(redisUrl, { maxRetriesPerRequest: 1 });
244
+ redis.on('error', () => undefined);
245
+ try {
246
+ const pong = await redis.ping();
247
+ results.push({
248
+ ok: pong === 'PONG',
249
+ level: pong === 'PONG' ? 'info' : 'error',
250
+ message: pong === 'PONG' ? 'Redis connection successful' : 'Redis ping failed',
251
+ });
252
+ } catch (error) {
253
+ results.push({
254
+ ok: false,
255
+ level: 'warn',
256
+ message: `Redis connection failed: ${error instanceof Error ? error.message : String(error)}`,
257
+ });
258
+ } finally {
259
+ await redis.quit().catch(() => undefined);
260
+ }
261
+ }
262
+
263
+ const hasMcpBuild = existsSync('dist/mcp/index.js');
264
+ const hasApiBuild = existsSync('dist/index.js');
265
+
266
+ if (mode === 'agent' || mode === 'full') {
267
+ if (hasMcpBuild) {
268
+ results.push({ ok: true, level: 'info', message: 'MCP server build exists (dist/mcp/index.js)' });
269
+ } else {
270
+ results.push({
271
+ ok: false,
272
+ level: 'error',
273
+ message: 'MCP server build is missing for agent/full mode (run `npm run build` first)',
274
+ });
275
+ }
276
+ } else if (hasMcpBuild) {
277
+ results.push({ ok: true, level: 'info', message: 'MCP server build exists (dist/mcp/index.js)' });
278
+ } else {
279
+ results.push({
280
+ ok: true,
281
+ level: 'warn',
282
+ message: 'MCP server build not found (optional for API-only validation)',
283
+ });
284
+ }
285
+
286
+ if (mode === 'api' || mode === 'full') {
287
+ if (hasApiBuild) {
288
+ results.push({ ok: true, level: 'info', message: 'API server build exists (dist/index.js)' });
289
+ } else {
290
+ results.push({
291
+ ok: false,
292
+ level: 'error',
293
+ message: 'API server build is missing for api/full mode (run `npm run build` first)',
294
+ });
295
+ }
296
+
297
+ results.push(await checkApiHealth(env));
298
+ } else if (hasApiBuild) {
299
+ results.push({ ok: true, level: 'info', message: 'API server build exists (dist/index.js)' });
300
+ } else {
301
+ results.push({
302
+ ok: true,
303
+ level: 'warn',
304
+ message: 'API server build not found (optional for agent-only validation)',
305
+ });
306
+ }
307
+
308
+ // .mcp.json check
309
+ if (existsSync('.mcp.json')) {
310
+ try {
311
+ const mcpConfig = JSON.parse(await readFile('.mcp.json', 'utf-8')) as unknown;
312
+ const mcpServers = isRecord(mcpConfig) && isRecord(mcpConfig.mcpServers) ? mcpConfig.mcpServers : null;
313
+ if (mcpServers && isRecord(mcpServers.supermemory)) {
314
+ results.push({ ok: true, level: 'info', message: '.mcp.json is valid with supermemory server configured' });
315
+ } else {
316
+ results.push({ ok: true, level: 'warn', message: '.mcp.json exists but missing supermemory server config' });
317
+ }
318
+ } catch {
319
+ results.push({ ok: false, level: 'warn', message: '.mcp.json exists but is not valid JSON' });
320
+ }
321
+ } else {
322
+ results.push({ ok: true, level: 'warn', message: '.mcp.json not found (optional for project-scope Claude auto-discovery)' });
323
+ }
324
+
325
+ // Claude Code MCP registration check
326
+ try {
327
+ const registrations = findClaudeMcpRegistrations('supermemory');
328
+ if (registrations.length > 0) {
329
+ const scopes = [...new Set(registrations.map((entry) => entry.scope))].join(', ');
330
+ results.push({ ok: true, level: 'info', message: `MCP server registered in Claude Code (${scopes} scope)` });
331
+ } else {
332
+ results.push({
333
+ ok: true,
334
+ level: 'warn',
335
+ message: 'MCP server not registered in Claude Code (optional; run `npm run mcp:setup` if you want Claude integration)',
336
+ });
337
+ }
338
+ } catch {
339
+ results.push({
340
+ ok: true,
341
+ level: 'warn',
342
+ message: 'Claude Code config could not be inspected (optional for MCP registration check)',
343
+ });
344
+ }
345
+
346
+ // Embedding API key check
347
+ if (hasOpenAIKey) {
348
+ results.push({ ok: true, level: 'info', message: 'OPENAI_API_KEY is configured' });
349
+ } else if (hasAnthropicKey) {
350
+ results.push({ ok: true, level: 'info', message: 'ANTHROPIC_API_KEY is configured' });
351
+ } else if (llmProvider === 'openai' || llmProvider === 'anthropic') {
352
+ results.push({
353
+ ok: true,
354
+ level: 'warn',
355
+ message: `LLM_PROVIDER=${llmProvider} is set but no real provider key is configured; local fallback behavior will be used`,
356
+ });
357
+ } else {
358
+ results.push({
359
+ ok: true,
360
+ level: 'warn',
361
+ message: 'No provider API keys are configured (embeddings and LLM features will use local fallback behavior)',
362
+ });
363
+ }
364
+
365
+ console.log('\nConfiguration checks:\n');
366
+ results.forEach(printResult);
367
+
368
+ const hasErrors = results.some((r) => !r.ok && r.level === 'error');
369
+ if (hasErrors) {
370
+ process.exit(1);
371
+ }
372
+ }
373
+
374
+ run().catch((error) => {
375
+ console.error('Doctor failed:', error);
376
+ process.exit(1);
377
+ });
@@ -0,0 +1,33 @@
1
+ -- =============================================================================
2
+ -- SuperMemory Clone - PostgreSQL Database Initialization
3
+ -- =============================================================================
4
+ -- This script is automatically executed when the PostgreSQL container starts
5
+ -- It ensures the pgvector extension is enabled and ready for vector operations
6
+ -- =============================================================================
7
+
8
+ -- Enable pgvector extension for vector similarity search
9
+ CREATE EXTENSION IF NOT EXISTS vector;
10
+
11
+ -- Verify installation
12
+ DO $$
13
+ BEGIN
14
+ IF NOT EXISTS (
15
+ SELECT 1 FROM pg_extension WHERE extname = 'vector'
16
+ ) THEN
17
+ RAISE EXCEPTION 'pgvector extension failed to install';
18
+ END IF;
19
+ END $$;
20
+
21
+ -- Test vector operations to ensure pgvector is working correctly
22
+ SELECT '[1,2,3]'::vector <-> '[4,5,6]'::vector AS test_distance;
23
+
24
+ -- =============================================================================
25
+ -- Additional PostgreSQL Optimization
26
+ -- =============================================================================
27
+
28
+ -- Enable essential extensions
29
+ CREATE EXTENSION IF NOT EXISTS plpgsql;
30
+
31
+ -- =============================================================================
32
+ -- Database ready for migration
33
+ -- =============================================================================